teamocil 1.0.5 → 1.0.6
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 +4 -4
- data/.rspec +1 -0
- data/.travis.yml +7 -2
- data/README.md +10 -0
- data/lib/teamocil.rb +10 -2
- data/lib/teamocil/command/list_panes.rb +15 -0
- data/lib/teamocil/command/list_windows.rb +9 -0
- data/lib/teamocil/command/show_options.rb +9 -0
- data/lib/teamocil/command/show_window_options.rb +9 -0
- data/lib/teamocil/error/invalid_yaml_layout.rb +13 -0
- data/lib/teamocil/error/layout_not_found.rb +13 -0
- data/lib/teamocil/layout.rb +10 -20
- data/lib/teamocil/tmux.rb +25 -0
- data/lib/teamocil/tmux/pane.rb +18 -4
- data/lib/teamocil/tmux/session.rb +3 -8
- data/lib/teamocil/tmux/window.rb +32 -9
- data/lib/teamocil/utils/closed_struct.rb +1 -1
- data/lib/teamocil/version.rb +1 -1
- data/spec/spec_helper.rb +16 -0
- data/spec/teamocil/cli_spec.rb +73 -0
- data/spec/teamocil/layout_spec.rb +126 -0
- data/spec/teamocil/tmux/pane_spec.rb +41 -0
- data/spec/teamocil/tmux/session_spec.rb +5 -0
- data/spec/teamocil/tmux/window_spec.rb +122 -0
- data/teamocil.gemspec +2 -1
- metadata +39 -6
- data/lib/teamocil/tmux/options.rb +0 -15
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3243fbfc89942819b6f281a12b5618ef90bb408
|
4
|
+
data.tar.gz: 598b276ff2b771918704058021d5264e5864348a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c6506fa11e9cbb7a1a0de10ac307caac52b1844132e005fd8a6763a39b780edb2c0c7c5c4d92e3234494f80d4c2723b0b11b586aad09f601d4d64be1535bb93a
|
7
|
+
data.tar.gz: 91a84c4b94ce363dc0736da6a9908cd30060af64debc49dd72af0d90d222a4e698582ee6b7d232541c5e538aba6d8a2c132d5160f5efdbe7d1a95713086b1941
|
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -68,6 +68,13 @@ Feel free to fork the repository and add back as many features as you want :)
|
|
68
68
|
|
69
69
|
## Configuration
|
70
70
|
|
71
|
+
### Session
|
72
|
+
|
73
|
+
| Key | Description
|
74
|
+
|-----------|----------------------------
|
75
|
+
| `name` | The tmux session name
|
76
|
+
| `windows` | An `Array` of windows
|
77
|
+
|
71
78
|
### Windows
|
72
79
|
|
73
80
|
| Key | Description
|
@@ -183,6 +190,9 @@ To get autocompletion when typing `teamocil <Tab>` in a zsh session, add this li
|
|
183
190
|
compctl -g '~/.teamocil/*(:t:r)' teamocil
|
184
191
|
```
|
185
192
|
|
193
|
+
[zsh-completions](https://github.com/zsh-users/zsh-completions) also provides
|
194
|
+
additional completion definitions for Teamocil.
|
195
|
+
|
186
196
|
### Bash autocompletion
|
187
197
|
|
188
198
|
To get autocompletion when typing `teamocil <Tab>` in a bash session, add this line to your `~/.bashrc` file:
|
data/lib/teamocil.rb
CHANGED
@@ -12,7 +12,13 @@ require 'teamocil/utils/option_parser'
|
|
12
12
|
require 'teamocil/layout'
|
13
13
|
require 'teamocil/cli'
|
14
14
|
|
15
|
+
# Errors
|
16
|
+
require 'teamocil/error/layout_not_found'
|
17
|
+
require 'teamocil/error/invalid_yaml_layout'
|
18
|
+
|
15
19
|
# Command classes
|
20
|
+
require 'teamocil/command/list_panes'
|
21
|
+
require 'teamocil/command/list_windows'
|
16
22
|
require 'teamocil/command/new_window'
|
17
23
|
require 'teamocil/command/rename_session'
|
18
24
|
require 'teamocil/command/rename_window'
|
@@ -21,13 +27,15 @@ require 'teamocil/command/select_pane'
|
|
21
27
|
require 'teamocil/command/select_window'
|
22
28
|
require 'teamocil/command/send_keys'
|
23
29
|
require 'teamocil/command/send_keys_to_pane'
|
30
|
+
require 'teamocil/command/show_options'
|
31
|
+
require 'teamocil/command/show_window_options'
|
24
32
|
require 'teamocil/command/split_window'
|
25
33
|
|
26
34
|
# Tmux classes
|
35
|
+
require 'teamocil/tmux'
|
27
36
|
require 'teamocil/tmux/session'
|
28
37
|
require 'teamocil/tmux/window'
|
29
38
|
require 'teamocil/tmux/pane'
|
30
|
-
require 'teamocil/tmux/options'
|
31
39
|
|
32
40
|
module Teamocil
|
33
41
|
class << self
|
@@ -49,7 +57,7 @@ module Teamocil
|
|
49
57
|
end
|
50
58
|
|
51
59
|
def self.query_system(command)
|
52
|
-
|
60
|
+
`tmux #{command.to_s}`
|
53
61
|
end
|
54
62
|
|
55
63
|
def self.parse_options!(arguments: nil)
|
data/lib/teamocil/layout.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
module Teamocil
|
2
|
-
class Layout < ClosedStruct.new(:path
|
2
|
+
class Layout < ClosedStruct.new(:path)
|
3
3
|
def execute!
|
4
4
|
if Teamocil.options[:debug]
|
5
5
|
Teamocil.puts(shell_commands.join("\n"))
|
@@ -34,33 +34,23 @@ module Teamocil
|
|
34
34
|
private
|
35
35
|
|
36
36
|
def shell_commands
|
37
|
-
|
38
|
-
commands.flatten.map { |command| "tmux #{command}" }
|
37
|
+
session.as_tmux.map { |command| "tmux #{command}" }
|
39
38
|
end
|
40
39
|
|
41
|
-
def
|
42
|
-
|
43
|
-
yaml_content = YAML.load(raw_content)
|
44
|
-
rescue
|
45
|
-
Teamocil.bail("There was a YAML error when parsing `#{path}`")
|
46
|
-
end
|
47
|
-
|
48
|
-
if valid?
|
49
|
-
Teamocil::Tmux::Session.new(yaml_content)
|
50
|
-
else
|
51
|
-
Teamocil.bail("The layout at `#{path}` is not valid.")
|
52
|
-
end
|
40
|
+
def session
|
41
|
+
Teamocil::Tmux::Session.new(parsed_content)
|
53
42
|
end
|
54
43
|
|
55
|
-
def
|
56
|
-
|
57
|
-
|
44
|
+
def parsed_content
|
45
|
+
YAML.load(raw_content)
|
46
|
+
rescue Psych::SyntaxError
|
47
|
+
raise Teamocil::Error::InvalidYAMLLayout, path
|
58
48
|
end
|
59
49
|
|
60
50
|
def raw_content
|
61
51
|
File.read(path)
|
62
|
-
rescue
|
63
|
-
Teamocil
|
52
|
+
rescue Errno::ENOENT
|
53
|
+
raise Teamocil::Error::LayoutNotFound, path
|
64
54
|
end
|
65
55
|
end
|
66
56
|
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Teamocil
|
2
|
+
module Tmux
|
3
|
+
def self.option(option, default: nil)
|
4
|
+
command = Teamocil::Command::ShowOptions.new(name: option)
|
5
|
+
value = Teamocil.query_system(command).chomp
|
6
|
+
value.empty? ? default : value.to_i
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.window_option(option, default: nil)
|
10
|
+
command = Teamocil::Command::ShowWindowOptions.new(name: option)
|
11
|
+
value = Teamocil.query_system(command).chomp
|
12
|
+
value.empty? ? default : value.to_i
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.window_count
|
16
|
+
command = Teamocil::Command::ListWindows.new
|
17
|
+
Teamocil.query_system(command).chomp.split("\n").size
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.pane_count(index: nil)
|
21
|
+
command = Teamocil::Command::ListPanes.new(index: index)
|
22
|
+
Teamocil.query_system(command).chomp.split("\n").size
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/teamocil/tmux/pane.rb
CHANGED
@@ -1,13 +1,27 @@
|
|
1
1
|
module Teamocil
|
2
2
|
module Tmux
|
3
|
-
class Pane < ClosedStruct.new(:index, :root, :commands, :focus
|
3
|
+
class Pane < ClosedStruct.new(:index, :root, :commands, :focus)
|
4
4
|
def as_tmux
|
5
5
|
[].tap do |tmux|
|
6
|
-
tmux << Teamocil::Command::SplitWindow.new(root: root) unless first
|
7
|
-
tmux << Teamocil::Command::SendKeysToPane.new(index:
|
8
|
-
tmux << Teamocil::Command::SendKeysToPane.new(index:
|
6
|
+
tmux << Teamocil::Command::SplitWindow.new(root: root) unless first?
|
7
|
+
tmux << Teamocil::Command::SendKeysToPane.new(index: internal_index, keys: commands.join('; ')) if commands
|
8
|
+
tmux << Teamocil::Command::SendKeysToPane.new(index: internal_index, keys: 'Enter')
|
9
9
|
end
|
10
10
|
end
|
11
|
+
|
12
|
+
def internal_index
|
13
|
+
index + self.class.pane_base_index
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.pane_base_index
|
17
|
+
@pane_base_index ||= Teamocil::Tmux.window_option('pane-base-index', default: 0)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def first?
|
23
|
+
index.zero?
|
24
|
+
end
|
11
25
|
end
|
12
26
|
end
|
13
27
|
end
|
@@ -9,8 +9,7 @@ module Teamocil
|
|
9
9
|
|
10
10
|
self.windows = windows.each_with_index.map do |window, index|
|
11
11
|
# Windows need to know their position
|
12
|
-
window.merge! index: index
|
13
|
-
window.merge! first: index.zero?
|
12
|
+
window.merge! index: index
|
14
13
|
|
15
14
|
Teamocil::Tmux::Window.new(window)
|
16
15
|
end
|
@@ -23,12 +22,8 @@ module Teamocil
|
|
23
22
|
|
24
23
|
# Set the focus on the right window or do nothing
|
25
24
|
focused_window = windows.find(&:focus)
|
26
|
-
tmux << Teamocil::Command::SelectWindow.new(index: focused_window.
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def window_base_index
|
31
|
-
@window_base_index ||= Teamocil::Tmux::Options.fetch_option('base-index', default: 0)
|
25
|
+
tmux << Teamocil::Command::SelectWindow.new(index: focused_window.internal_index) if focused_window
|
26
|
+
end.flatten
|
32
27
|
end
|
33
28
|
end
|
34
29
|
end
|
data/lib/teamocil/tmux/window.rb
CHANGED
@@ -1,20 +1,19 @@
|
|
1
1
|
module Teamocil
|
2
2
|
module Tmux
|
3
|
-
class Window < ClosedStruct.new(:index, :root, :focus, :layout, :name, :panes
|
3
|
+
class Window < ClosedStruct.new(:index, :root, :focus, :layout, :name, :panes)
|
4
4
|
def initialize(object)
|
5
5
|
super
|
6
6
|
|
7
7
|
# Make sure paths like `~/foo/bar` work
|
8
8
|
self.root = File.expand_path(root) if root
|
9
9
|
|
10
|
-
self.panes ||=
|
10
|
+
self.panes ||= []
|
11
11
|
self.panes = panes.each_with_index.map do |pane, index|
|
12
12
|
# Support single command instead of `commands` key in Hash
|
13
13
|
pane = { commands: [pane] } if pane.is_a?(String)
|
14
14
|
|
15
15
|
# Panes need to know their position
|
16
|
-
pane.merge! index: index
|
17
|
-
pane.merge! first: index.zero?
|
16
|
+
pane.merge! index: index
|
18
17
|
|
19
18
|
# Panes need know the window root directory
|
20
19
|
pane.merge! root: root
|
@@ -26,27 +25,51 @@ module Teamocil
|
|
26
25
|
def as_tmux
|
27
26
|
[].tap do |tmux|
|
28
27
|
# Rename the current window or create a new one
|
29
|
-
if Teamocil.options[:here] && first
|
28
|
+
if Teamocil.options[:here] && first?
|
29
|
+
if root
|
30
|
+
first_pane_index = panes.first.internal_index
|
31
|
+
tmux << Teamocil::Command::SendKeysToPane.new(index: first_pane_index, keys: %(cd "#{root}"))
|
32
|
+
tmux << Teamocil::Command::SendKeysToPane.new(index: first_pane_index, keys: 'Enter')
|
33
|
+
end
|
34
|
+
|
30
35
|
tmux << Teamocil::Command::RenameWindow.new(name: name)
|
31
36
|
else
|
32
37
|
tmux << Teamocil::Command::NewWindow.new(name: name, root: root)
|
33
38
|
end
|
34
39
|
|
35
40
|
# Execute all panes commands
|
36
|
-
tmux << panes.map(&:as_tmux)
|
41
|
+
tmux << panes.map(&:as_tmux).flatten
|
37
42
|
|
38
43
|
# Select the window layout
|
39
44
|
tmux << Teamocil::Command::SelectLayout.new(layout: layout) if layout
|
40
45
|
|
41
46
|
# Set the focus on the right pane or the first one
|
42
47
|
focused_pane = panes.find(&:focus)
|
43
|
-
focused_index = focused_pane ? focused_pane.
|
48
|
+
focused_index = focused_pane ? focused_pane.internal_index : Teamocil::Tmux::Pane.pane_base_index
|
44
49
|
tmux << Teamocil::Command::SelectPane.new(index: focused_index)
|
50
|
+
end.flatten
|
51
|
+
end
|
52
|
+
|
53
|
+
def internal_index
|
54
|
+
index + self.class.window_base_index
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.window_base_index
|
58
|
+
@window_base_index ||= begin
|
59
|
+
base_index = Teamocil::Tmux.option('base-index', default: 0)
|
60
|
+
current_window_count = Teamocil::Tmux.window_count
|
61
|
+
|
62
|
+
# If `--here` is specified, treat the current window as a new one
|
63
|
+
current_window_count -= 1 if Teamocil.options[:here]
|
64
|
+
|
65
|
+
base_index + current_window_count
|
45
66
|
end
|
46
67
|
end
|
47
68
|
|
48
|
-
|
49
|
-
|
69
|
+
protected
|
70
|
+
|
71
|
+
def first?
|
72
|
+
index.zero?
|
50
73
|
end
|
51
74
|
end
|
52
75
|
end
|
@@ -5,7 +5,7 @@ module Teamocil
|
|
5
5
|
|
6
6
|
args.first.each_pair do |key, value|
|
7
7
|
# Make sure we only set values to defined arguments
|
8
|
-
if members.map
|
8
|
+
if members.map(&:intern).include?(key.to_sym)
|
9
9
|
send "#{key}=", value
|
10
10
|
else
|
11
11
|
raise ArgumentError, "#{self.class.name} doesn’t support the `#{key}` keyword, only #{members.join(', ')}"
|
data/lib/teamocil/version.rb
CHANGED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path('../lib', __FILE__)
|
2
|
+
|
3
|
+
# Ruby stdlib dependencies
|
4
|
+
require 'tmpdir'
|
5
|
+
require 'fileutils'
|
6
|
+
|
7
|
+
# RSpec testing framework
|
8
|
+
require 'rspec'
|
9
|
+
|
10
|
+
# Teamocil
|
11
|
+
require 'teamocil'
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
# Disable `should` syntax
|
15
|
+
config.expect_with(:rspec) { |c| c.syntax = :expect }
|
16
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Teamocil::CLI do
|
4
|
+
describe :run! do
|
5
|
+
let(:cli) { Teamocil::CLI.new(arguments: arguments, environment: environment) }
|
6
|
+
let(:environment) { { 'HOME' => Dir.tmpdir } }
|
7
|
+
let(:root) { File.join(environment['HOME'], '.teamocil') }
|
8
|
+
|
9
|
+
describe 'with --list option' do
|
10
|
+
let(:arguments) { ['--list'] }
|
11
|
+
|
12
|
+
before do
|
13
|
+
expect(Teamocil::Layout).to receive(:print_available_layouts).with(directory: root)
|
14
|
+
end
|
15
|
+
|
16
|
+
specify { cli.run! }
|
17
|
+
end
|
18
|
+
|
19
|
+
describe 'with --show option' do
|
20
|
+
let(:arguments) { ['--show', 'foo'] }
|
21
|
+
let(:layout) { double(Teamocil::Layout) }
|
22
|
+
let(:path) { File.join(root, 'foo.yml') }
|
23
|
+
|
24
|
+
before do
|
25
|
+
expect(Teamocil::Layout).to receive(:new).with(path: path).and_return(layout)
|
26
|
+
expect(layout).to receive(:show!)
|
27
|
+
end
|
28
|
+
|
29
|
+
specify { cli.run! }
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'with --edit option' do
|
33
|
+
let(:arguments) { ['--edit', 'foo'] }
|
34
|
+
let(:layout) { double(Teamocil::Layout) }
|
35
|
+
let(:path) { File.join(root, 'foo.yml') }
|
36
|
+
|
37
|
+
before do
|
38
|
+
expect(Teamocil::Layout).to receive(:new).with(path: path).and_return(layout)
|
39
|
+
expect(layout).to receive(:edit!)
|
40
|
+
end
|
41
|
+
|
42
|
+
specify { cli.run! }
|
43
|
+
end
|
44
|
+
|
45
|
+
describe 'with implicit execution' do
|
46
|
+
context 'with --layout option' do
|
47
|
+
let(:arguments) { ['--layout', path] }
|
48
|
+
let(:layout) { double(Teamocil::Layout) }
|
49
|
+
let(:path) { File.join(root, 'bar.yml') }
|
50
|
+
|
51
|
+
before do
|
52
|
+
expect(Teamocil::Layout).to receive(:new).with(path: path).and_return(layout)
|
53
|
+
expect(layout).to receive(:execute!)
|
54
|
+
end
|
55
|
+
|
56
|
+
specify { cli.run! }
|
57
|
+
end
|
58
|
+
|
59
|
+
context 'without --layout option' do
|
60
|
+
let(:arguments) { ['foo'] }
|
61
|
+
let(:layout) { double(Teamocil::Layout) }
|
62
|
+
let(:path) { File.join(root, 'foo.yml') }
|
63
|
+
|
64
|
+
before do
|
65
|
+
expect(Teamocil::Layout).to receive(:new).with(path: path).and_return(layout)
|
66
|
+
expect(layout).to receive(:execute!)
|
67
|
+
end
|
68
|
+
|
69
|
+
specify { cli.run! }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Teamocil::Layout do
|
4
|
+
describe :execute! do
|
5
|
+
let(:layout) { Teamocil::Layout.new(path: path) }
|
6
|
+
let(:execute!) { layout.execute! }
|
7
|
+
let(:path) { double('Path') }
|
8
|
+
|
9
|
+
let(:options) { {} }
|
10
|
+
before { allow(Teamocil).to receive(:options).and_return(options) }
|
11
|
+
|
12
|
+
context 'without errors' do
|
13
|
+
let(:raw_content) { double('Raw content') }
|
14
|
+
let(:parsed_content) { double('Parsed content') }
|
15
|
+
let(:session) { instance_double(Teamocil::Tmux::Session) }
|
16
|
+
let(:commands) { %w(foo bar omg) }
|
17
|
+
|
18
|
+
before do
|
19
|
+
allow(Teamocil::Tmux::Session).to receive(:new).and_return(session)
|
20
|
+
|
21
|
+
expect(File).to receive(:read).with(path).and_return(raw_content)
|
22
|
+
expect(YAML).to receive(:load).with(raw_content).and_return(parsed_content)
|
23
|
+
|
24
|
+
expect(session).to receive(:as_tmux).and_return(commands)
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'without --debug option' do
|
28
|
+
before do
|
29
|
+
expect(Teamocil).to receive(:system).with('tmux foo; tmux bar; tmux omg')
|
30
|
+
end
|
31
|
+
|
32
|
+
specify { layout.execute! }
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'with --debug option' do
|
36
|
+
let(:options) { { debug: true } }
|
37
|
+
|
38
|
+
before do
|
39
|
+
expect(Teamocil).to receive(:puts).with("tmux foo\ntmux bar\ntmux omg")
|
40
|
+
end
|
41
|
+
|
42
|
+
specify { execute! }
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'with not-found layout' do
|
47
|
+
before do
|
48
|
+
# Let’s simulate an error from `File.read`
|
49
|
+
expect(File).to receive(:read).with(path) { raise Errno::ENOENT }
|
50
|
+
end
|
51
|
+
|
52
|
+
it { expect { execute! }.to raise_error(Teamocil::Error::LayoutNotFound) }
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'with invalid YAML layout' do
|
56
|
+
let(:raw_content) { double('Raw content') }
|
57
|
+
|
58
|
+
before do
|
59
|
+
expect(File).to receive(:read).with(path).and_return(raw_content)
|
60
|
+
|
61
|
+
# Let’s simulate an error from `YAML.load`
|
62
|
+
expect(YAML).to receive(:load).with(raw_content) { raise Psych::SyntaxError.new(nil, 0, 0, 0, nil, nil) }
|
63
|
+
end
|
64
|
+
|
65
|
+
it { expect { execute! }.to raise_error(Teamocil::Error::InvalidYAMLLayout) }
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe :show! do
|
70
|
+
let(:layout) { Teamocil::Layout.new(path: path) }
|
71
|
+
let(:show!) { layout.show! }
|
72
|
+
let(:path) { double('Path') }
|
73
|
+
let(:raw_content) { double('Raw content') }
|
74
|
+
|
75
|
+
context 'with found layout' do
|
76
|
+
before do
|
77
|
+
expect(File).to receive(:read).with(path).and_return(raw_content)
|
78
|
+
expect(Teamocil).to receive(:puts).with(raw_content)
|
79
|
+
end
|
80
|
+
|
81
|
+
specify { show! }
|
82
|
+
end
|
83
|
+
|
84
|
+
context 'with not-found layout' do
|
85
|
+
before do
|
86
|
+
# Let’s simulate an error from `File.read`
|
87
|
+
expect(File).to receive(:read).with(path) { raise Errno::ENOENT }
|
88
|
+
end
|
89
|
+
|
90
|
+
it { expect { show! }.to raise_error(Teamocil::Error::LayoutNotFound) }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe :edit! do
|
95
|
+
let(:layout) { Teamocil::Layout.new(path: path) }
|
96
|
+
let(:edit!) { layout.edit! }
|
97
|
+
let(:path) { double('Path') }
|
98
|
+
|
99
|
+
before do
|
100
|
+
expect(Teamocil).to receive(:system).with("$EDITOR #{path}")
|
101
|
+
end
|
102
|
+
|
103
|
+
specify { edit! }
|
104
|
+
end
|
105
|
+
|
106
|
+
describe :ClassMethods do
|
107
|
+
describe :print_available_layouts do
|
108
|
+
let(:tmpdir) { Dir.mktmpdir }
|
109
|
+
let(:directory) { File.join(tmpdir, '.teamocil') }
|
110
|
+
after { FileUtils.remove_entry(tmpdir) }
|
111
|
+
|
112
|
+
before do
|
113
|
+
FileUtils.mkdir_p(directory)
|
114
|
+
FileUtils.touch(File.join(directory, 'foo.yml'))
|
115
|
+
FileUtils.touch(File.join(directory, 'bar.yml'))
|
116
|
+
FileUtils.touch(File.join(directory, 'omg.txt'))
|
117
|
+
|
118
|
+
expect(Teamocil).to receive(:puts).with(%w(bar foo))
|
119
|
+
end
|
120
|
+
|
121
|
+
specify do
|
122
|
+
Teamocil::Layout.print_available_layouts(directory: directory)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Teamocil::Tmux::Pane do
|
4
|
+
describe :as_tmux do
|
5
|
+
let(:pane) { Teamocil::Tmux::Pane.new(commands: commands, root: root, index: index, focus: focus) }
|
6
|
+
let(:as_tmux) { pane.as_tmux }
|
7
|
+
let(:pane_base_index) { Random.rand(0..100) }
|
8
|
+
|
9
|
+
before do
|
10
|
+
allow(Teamocil::Tmux::Pane).to receive(:pane_base_index).and_return(pane_base_index)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Pane attributes
|
14
|
+
let(:commands) { %w(foo bar) }
|
15
|
+
let(:root) { '/tmp' }
|
16
|
+
let(:focus) { true }
|
17
|
+
|
18
|
+
context 'for first pane' do
|
19
|
+
let(:index) { 0 }
|
20
|
+
|
21
|
+
it do
|
22
|
+
expect(as_tmux).to eql [
|
23
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index, keys: 'foo; bar'),
|
24
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index, keys: 'Enter')
|
25
|
+
]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'for not-first pane' do
|
30
|
+
let(:index) { 1 }
|
31
|
+
|
32
|
+
it do
|
33
|
+
expect(as_tmux).to eql [
|
34
|
+
Teamocil::Command::SplitWindow.new(root: root),
|
35
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index + index, keys: 'foo; bar'),
|
36
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index + index, keys: 'Enter')
|
37
|
+
]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Teamocil::Tmux::Window do
|
4
|
+
let(:window) { Teamocil::Tmux::Window.new(index: index, root: root, layout: layout, name: name, panes: panes) }
|
5
|
+
let(:as_tmux) { window.as_tmux }
|
6
|
+
|
7
|
+
before do
|
8
|
+
allow(Teamocil::Tmux::Pane).to receive(:pane_base_index).and_return(pane_base_index)
|
9
|
+
allow(Teamocil::Tmux::Window).to receive(:window_base_index).and_return(window_base_index)
|
10
|
+
allow(Teamocil).to receive(:options).and_return(options)
|
11
|
+
end
|
12
|
+
|
13
|
+
# Tmux options
|
14
|
+
let(:pane_base_index) { Random.rand(0..100) }
|
15
|
+
let(:window_base_index) { Random.rand(0..100) }
|
16
|
+
|
17
|
+
# Window attributes
|
18
|
+
let(:options) { {} }
|
19
|
+
let(:index) { 0 }
|
20
|
+
let(:name) { 'foo' }
|
21
|
+
let(:layout) { nil }
|
22
|
+
let(:root) { nil }
|
23
|
+
let(:panes) do
|
24
|
+
[
|
25
|
+
{ commands: %w(foo omg) },
|
26
|
+
{ commands: %w(bar), focus: true }
|
27
|
+
]
|
28
|
+
end
|
29
|
+
|
30
|
+
it do
|
31
|
+
expect(as_tmux).to eql [
|
32
|
+
Teamocil::Command::NewWindow.new(name: name),
|
33
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index, keys: 'foo; omg'),
|
34
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index, keys: 'Enter'),
|
35
|
+
Teamocil::Command::SplitWindow.new,
|
36
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index + 1, keys: 'bar'),
|
37
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index + 1, keys: 'Enter'),
|
38
|
+
Teamocil::Command::SelectPane.new(index: pane_base_index + 1)
|
39
|
+
]
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'with root attribute' do
|
43
|
+
let(:root) { '/tmp' }
|
44
|
+
|
45
|
+
it do
|
46
|
+
expect(as_tmux).to eql [
|
47
|
+
Teamocil::Command::NewWindow.new(name: name, root: root),
|
48
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index, keys: 'foo; omg'),
|
49
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index, keys: 'Enter'),
|
50
|
+
Teamocil::Command::SplitWindow.new(root: root),
|
51
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index + 1, keys: 'bar'),
|
52
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index + 1, keys: 'Enter'),
|
53
|
+
Teamocil::Command::SelectPane.new(index: pane_base_index + 1)
|
54
|
+
]
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'without panes' do
|
59
|
+
let(:panes) { nil }
|
60
|
+
|
61
|
+
it do
|
62
|
+
expect(as_tmux).to eql [
|
63
|
+
Teamocil::Command::NewWindow.new(name: name, root: root),
|
64
|
+
Teamocil::Command::SelectPane.new(index: pane_base_index)
|
65
|
+
]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'with layout attribute' do
|
70
|
+
let(:layout) { 'tiled' }
|
71
|
+
|
72
|
+
it do
|
73
|
+
expect(as_tmux).to eql [
|
74
|
+
Teamocil::Command::NewWindow.new(name: name),
|
75
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index, keys: 'foo; omg'),
|
76
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index, keys: 'Enter'),
|
77
|
+
Teamocil::Command::SplitWindow.new,
|
78
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index + 1, keys: 'bar'),
|
79
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index + 1, keys: 'Enter'),
|
80
|
+
Teamocil::Command::SelectLayout.new(layout: layout),
|
81
|
+
Teamocil::Command::SelectPane.new(index: pane_base_index + 1)
|
82
|
+
]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context 'with --here option' do
|
87
|
+
context 'without root' do
|
88
|
+
let(:options) { { here: true } }
|
89
|
+
|
90
|
+
it do
|
91
|
+
expect(as_tmux).to eql [
|
92
|
+
Teamocil::Command::RenameWindow.new(name: name),
|
93
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index, keys: 'foo; omg'),
|
94
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index, keys: 'Enter'),
|
95
|
+
Teamocil::Command::SplitWindow.new,
|
96
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index + 1, keys: 'bar'),
|
97
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index + 1, keys: 'Enter'),
|
98
|
+
Teamocil::Command::SelectPane.new(index: pane_base_index + 1)
|
99
|
+
]
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context 'with root' do
|
104
|
+
let(:root) { '/tmp' }
|
105
|
+
let(:options) { { here: true } }
|
106
|
+
|
107
|
+
it do
|
108
|
+
expect(as_tmux).to eql [
|
109
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index, keys: 'cd "/tmp"'),
|
110
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index, keys: 'Enter'),
|
111
|
+
Teamocil::Command::RenameWindow.new(name: name),
|
112
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index, keys: 'foo; omg'),
|
113
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index, keys: 'Enter'),
|
114
|
+
Teamocil::Command::SplitWindow.new(root: root),
|
115
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index + 1, keys: 'bar'),
|
116
|
+
Teamocil::Command::SendKeysToPane.new(index: pane_base_index + 1, keys: 'Enter'),
|
117
|
+
Teamocil::Command::SelectPane.new(index: pane_base_index + 1)
|
118
|
+
]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
data/teamocil.gemspec
CHANGED
@@ -24,5 +24,6 @@ Gem::Specification.new do |s|
|
|
24
24
|
# Dependencies
|
25
25
|
s.add_development_dependency 'rake'
|
26
26
|
s.add_development_dependency 'phare', '~> 0.6'
|
27
|
-
s.add_development_dependency 'rubocop', '
|
27
|
+
s.add_development_dependency 'rubocop', '0.26.1'
|
28
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
28
29
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: teamocil
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rémi Prévost
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-09-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -40,18 +40,32 @@ dependencies:
|
|
40
40
|
version: '0.6'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rubocop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.26.1
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.26.1
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
59
|
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
|
-
version: '0
|
61
|
+
version: '3.0'
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
66
|
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
|
-
version: '0
|
68
|
+
version: '3.0'
|
55
69
|
description: Teamocil helps you set up window and pane layouts for tmux using YAML
|
56
70
|
configuration files.
|
57
71
|
email: remi@exomel.com
|
@@ -61,6 +75,7 @@ extensions: []
|
|
61
75
|
extra_rdoc_files: []
|
62
76
|
files:
|
63
77
|
- ".gitignore"
|
78
|
+
- ".rspec"
|
64
79
|
- ".rubocop.yml"
|
65
80
|
- ".travis.yml"
|
66
81
|
- Gemfile
|
@@ -70,6 +85,8 @@ files:
|
|
70
85
|
- bin/teamocil
|
71
86
|
- lib/teamocil.rb
|
72
87
|
- lib/teamocil/cli.rb
|
88
|
+
- lib/teamocil/command/list_panes.rb
|
89
|
+
- lib/teamocil/command/list_windows.rb
|
73
90
|
- lib/teamocil/command/new_window.rb
|
74
91
|
- lib/teamocil/command/rename_session.rb
|
75
92
|
- lib/teamocil/command/rename_window.rb
|
@@ -78,15 +95,25 @@ files:
|
|
78
95
|
- lib/teamocil/command/select_window.rb
|
79
96
|
- lib/teamocil/command/send_keys.rb
|
80
97
|
- lib/teamocil/command/send_keys_to_pane.rb
|
98
|
+
- lib/teamocil/command/show_options.rb
|
99
|
+
- lib/teamocil/command/show_window_options.rb
|
81
100
|
- lib/teamocil/command/split_window.rb
|
101
|
+
- lib/teamocil/error/invalid_yaml_layout.rb
|
102
|
+
- lib/teamocil/error/layout_not_found.rb
|
82
103
|
- lib/teamocil/layout.rb
|
83
|
-
- lib/teamocil/tmux
|
104
|
+
- lib/teamocil/tmux.rb
|
84
105
|
- lib/teamocil/tmux/pane.rb
|
85
106
|
- lib/teamocil/tmux/session.rb
|
86
107
|
- lib/teamocil/tmux/window.rb
|
87
108
|
- lib/teamocil/utils/closed_struct.rb
|
88
109
|
- lib/teamocil/utils/option_parser.rb
|
89
110
|
- lib/teamocil/version.rb
|
111
|
+
- spec/spec_helper.rb
|
112
|
+
- spec/teamocil/cli_spec.rb
|
113
|
+
- spec/teamocil/layout_spec.rb
|
114
|
+
- spec/teamocil/tmux/pane_spec.rb
|
115
|
+
- spec/teamocil/tmux/session_spec.rb
|
116
|
+
- spec/teamocil/tmux/window_spec.rb
|
90
117
|
- teamocil.gemspec
|
91
118
|
homepage: http://remiprev.github.com/teamocil
|
92
119
|
licenses:
|
@@ -112,4 +139,10 @@ rubygems_version: 2.2.2
|
|
112
139
|
signing_key:
|
113
140
|
specification_version: 4
|
114
141
|
summary: Easy session, window and pane layouts for tmux
|
115
|
-
test_files:
|
142
|
+
test_files:
|
143
|
+
- spec/spec_helper.rb
|
144
|
+
- spec/teamocil/cli_spec.rb
|
145
|
+
- spec/teamocil/layout_spec.rb
|
146
|
+
- spec/teamocil/tmux/pane_spec.rb
|
147
|
+
- spec/teamocil/tmux/session_spec.rb
|
148
|
+
- spec/teamocil/tmux/window_spec.rb
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module Teamocil
|
2
|
-
module Tmux
|
3
|
-
class Options
|
4
|
-
def self.fetch_option(option, default: nil)
|
5
|
-
value = Teamocil.query_system("tmux show-options -gv #{option}").chomp
|
6
|
-
value.empty? ? default : value.to_i
|
7
|
-
end
|
8
|
-
|
9
|
-
def self.fetch_window_option(option, default: nil)
|
10
|
-
value = Teamocil.query_system("tmux show-window-options -gv #{option}").chomp
|
11
|
-
value.empty? ? default : value.to_i
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|