teamocil 0.4.5 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +14 -4
  3. data/.rubocop.yml +48 -0
  4. data/.travis.yml +2 -2
  5. data/Gemfile +1 -1
  6. data/{LICENSE → LICENSE.md} +0 -0
  7. data/README.md +124 -166
  8. data/Rakefile +12 -27
  9. data/bin/teamocil +3 -4
  10. data/lib/teamocil.rb +51 -4
  11. data/lib/teamocil/cli.rb +23 -90
  12. data/lib/teamocil/command/new_window.rb +16 -0
  13. data/lib/teamocil/command/rename_session.rb +9 -0
  14. data/lib/teamocil/command/rename_window.rb +9 -0
  15. data/lib/teamocil/command/select_layout.rb +9 -0
  16. data/lib/teamocil/command/select_pane.rb +9 -0
  17. data/lib/teamocil/command/select_window.rb +9 -0
  18. data/lib/teamocil/command/send_keys.rb +9 -0
  19. data/lib/teamocil/command/send_keys_to_pane.rb +9 -0
  20. data/lib/teamocil/command/split_window.rb +15 -0
  21. data/lib/teamocil/layout.rb +58 -36
  22. data/lib/teamocil/tmux/pane.rb +15 -0
  23. data/lib/teamocil/tmux/session.rb +28 -0
  24. data/lib/teamocil/tmux/window.rb +47 -0
  25. data/lib/teamocil/utils/closed_struct.rb +16 -0
  26. data/lib/teamocil/utils/option_parser.rb +56 -0
  27. data/lib/teamocil/version.rb +1 -1
  28. data/teamocil.gemspec +15 -16
  29. metadata +27 -54
  30. data/examples/four-splits.yml +0 -8
  31. data/examples/one-and-three-splits.yml +0 -8
  32. data/examples/six-splits.yml +0 -10
  33. data/examples/two-horizontal-splits.yml +0 -6
  34. data/examples/two-vertical-splits.yml +0 -6
  35. data/lib/teamocil/error.rb +0 -6
  36. data/lib/teamocil/layout/pane.rb +0 -66
  37. data/lib/teamocil/layout/session.rb +0 -30
  38. data/lib/teamocil/layout/window.rb +0 -77
  39. data/spec/cli_spec.rb +0 -79
  40. data/spec/fixtures/.my-fancy-layouts-directory/sample-3.yml +0 -10
  41. data/spec/fixtures/.teamocil/sample-2.yml +0 -10
  42. data/spec/fixtures/.teamocil/sample.yml +0 -10
  43. data/spec/fixtures/layouts.yml +0 -76
  44. data/spec/layout_spec.rb +0 -229
  45. data/spec/mock/cli.rb +0 -35
  46. data/spec/mock/layout.rb +0 -16
  47. data/spec/spec_helper.rb +0 -17
@@ -0,0 +1,15 @@
1
+ module Teamocil
2
+ class Pane < ClosedStruct.new(:index, :root, :commands, :focus)
3
+ def as_tmux
4
+ [].tap do |tmux|
5
+ tmux << Command::SplitWindow.new(root: root) unless first?
6
+ tmux << Command::SendKeysToPane.new(index: index, keys: commands.join('; '))
7
+ tmux << Command::SendKeysToPane.new(index: index, keys: 'Enter')
8
+ end
9
+ end
10
+
11
+ def first?
12
+ index == 1
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ module Teamocil
2
+ class Session < ClosedStruct.new(:name, :windows)
3
+ def initialize(object)
4
+ super
5
+
6
+ # Sessions need a name
7
+ self.name = "teamocil-session-#{rand(1_000_000)}" unless name
8
+
9
+ self.windows = windows.each_with_index.map do |window, index|
10
+ # Windows need to know their position
11
+ window.merge! index: index + 1
12
+
13
+ Window.new(window)
14
+ end
15
+ end
16
+
17
+ def as_tmux
18
+ [].tap do |tmux|
19
+ tmux << Command::RenameSession.new(name: name)
20
+ tmux << windows.map(&:as_tmux)
21
+
22
+ # Set the focus on the right window or do nothing
23
+ focused_window = windows.find(&:focus)
24
+ tmux << Command::SelectWindow.new(index: focused_window.index) if focused_window
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,47 @@
1
+ module Teamocil
2
+ class Window < ClosedStruct.new(:index, :root, :focus, :layout, :name, :panes)
3
+ def initialize(object)
4
+ super
5
+
6
+ self.panes ||= splits
7
+ self.panes = panes.each_with_index.map do |pane, index|
8
+ # Support single command instead of `commands` key in Hash
9
+ pane = { commands: [pane] } if pane.is_a?(String)
10
+
11
+ # Panes need to know their position
12
+ pane.merge! index: index + 1
13
+
14
+ # Panes need know the window root directory
15
+ pane.merge! root: root
16
+
17
+ Pane.new(pane)
18
+ end
19
+ end
20
+
21
+ def first?
22
+ index == 1
23
+ end
24
+
25
+ def as_tmux
26
+ [].tap do |tmux|
27
+ # Rename the current window or create a new one
28
+ if Teamocil.options[:here] && first?
29
+ tmux << Command::RenameWindow.new(name: name)
30
+ else
31
+ tmux << Command::NewWindow.new(name: name, root: root)
32
+ end
33
+
34
+ # Execute all panes commands
35
+ tmux << panes.map(&:as_tmux)
36
+
37
+ # Select the window layout
38
+ tmux << Command::SelectLayout.new(layout: layout)
39
+
40
+ # Set the focus on the right pane or the first one
41
+ focused_pane = panes.find(&:focus)
42
+ focused_index = focused_pane ? focused_pane.index : 0
43
+ tmux << Command::SelectPane.new(index: focused_index)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,16 @@
1
+ module Teamocil
2
+ class ClosedStruct < Struct
3
+ def initialize(*args)
4
+ args = [{}] unless args.any?
5
+
6
+ args.first.each_pair do |key, value|
7
+ # Make sure we only set values to defined arguments
8
+ if members.map { |x| x.intern }.include?(key.to_sym)
9
+ send "#{key}=", value
10
+ else
11
+ raise ArgumentError, "#{self.class.name} doesn’t support the `#{key}` keyword, only #{members.join(', ')}"
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,56 @@
1
+ module Teamocil
2
+ class OptionParser
3
+ def initialize(arguments:)
4
+ @arguments = arguments
5
+ @parsed_options = {}
6
+ end
7
+
8
+ def parsed_options
9
+ @parsed_options.tap do
10
+ parser.parse!(@arguments)
11
+ end
12
+ end
13
+
14
+ # rubocop:disable MethodLength
15
+ def parser
16
+ ::OptionParser.new do |parser|
17
+ parser.banner = 'Usage: teamocil [options] <layout>'
18
+ parser.separator ''
19
+ parser.separator 'Specific options:'
20
+
21
+ # Global options
22
+ parser.on('--list', 'List all available layouts in `~/.teamocil/`') do
23
+ @parsed_options[:list] = true
24
+ end
25
+
26
+ # Single layout options
27
+ parser.on('--here', 'Set up the first layout window in the current tmux window') do
28
+ @parsed_options[:here] = true
29
+ end
30
+
31
+ parser.on('--layout [layout]', 'Use a specific layout file, instead of `~/.teamocil/<layout>.yml`') do |layout|
32
+ @parsed_options[:layout] = layout
33
+ end
34
+
35
+ parser.on('--edit', 'Edit the YAML layout file instead of using it') do
36
+ @parsed_options[:edit] = true
37
+ end
38
+
39
+ parser.on('--show', 'Show the content of the layout file instead of executing it') do
40
+ @parsed_options[:show] = true
41
+ end
42
+
43
+ # Debug options
44
+ parser.on('--debug', 'Show the commands Teamocil will execute instead of actually executing them') do
45
+ @parsed_options[:debug] = true
46
+ end
47
+
48
+ parser.on('--version', '-v', 'Show Teamocil’s version number') do
49
+ Teamocil.puts(VERSION)
50
+ exit
51
+ end
52
+ end
53
+ end
54
+ # rubocop:enable all
55
+ end
56
+ end
@@ -1,3 +1,3 @@
1
1
  module Teamocil
2
- VERSION = "0.4.5"
2
+ VERSION = '1.0'
3
3
  end
data/teamocil.gemspec CHANGED
@@ -1,29 +1,28 @@
1
1
  # encoding: utf-8
2
2
 
3
- $:.push File.expand_path("../lib", __FILE__)
4
- require "teamocil/version"
3
+ $LOAD_PATH.push File.expand_path('../lib', __FILE__)
4
+ require 'teamocil/version'
5
5
 
6
- spec = Gem::Specification.new do |s|
6
+ Gem::Specification.new do |s|
7
7
  # Metadata
8
- s.name = "teamocil"
8
+ s.name = 'teamocil'
9
9
  s.version = Teamocil::VERSION
10
10
  s.platform = Gem::Platform::RUBY
11
- s.authors = "Rémi Prévost"
12
- s.email = "remi@exomel.com"
13
- s.homepage = "http://remiprev.github.com/teamocil"
14
- s.license = "MIT"
15
- s.summary = "Easy session, window and pane layouts for tmux"
16
- s.description = "Teamocil helps you set up window and pane layouts for tmux using YAML configuration files."
11
+ s.authors = 'Rémi Prévost'
12
+ s.email = 'remi@exomel.com'
13
+ s.homepage = 'http://remiprev.github.com/teamocil'
14
+ s.license = 'MIT'
15
+ s.summary = 'Easy session, window and pane layouts for tmux'
16
+ s.description = 'Teamocil helps you set up window and pane layouts for tmux using YAML configuration files.'
17
17
 
18
18
  # Manifest
19
19
  s.files = `git ls-files`.split("\n")
20
20
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
21
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
22
- s.require_paths = ["lib"]
21
+ s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
22
+ s.require_paths = ['lib']
23
23
 
24
24
  # Dependencies
25
- s.add_development_dependency "rake"
26
- s.add_development_dependency "rspec", '~> 2.13'
27
- s.add_development_dependency "yard"
28
- s.add_development_dependency "maruku"
25
+ s.add_development_dependency 'rake'
26
+ s.add_development_dependency 'phare', '~> 0.6'
27
+ s.add_development_dependency 'rubocop', '~> 0.24'
29
28
  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: 0.4.5
4
+ version: '1.0'
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-06-18 00:00:00.000000000 Z
11
+ date: 2014-08-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -25,47 +25,33 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rspec
28
+ name: phare
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.13'
33
+ version: '0.6'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.13'
40
+ version: '0.6'
41
41
  - !ruby/object:Gem::Dependency
42
- name: yard
42
+ name: rubocop
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: maruku
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
45
+ - - "~>"
60
46
  - !ruby/object:Gem::Version
61
- version: '0'
47
+ version: '0.24'
62
48
  type: :development
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
- - - ">="
52
+ - - "~>"
67
53
  - !ruby/object:Gem::Version
68
- version: '0'
54
+ version: '0.24'
69
55
  description: Teamocil helps you set up window and pane layouts for tmux using YAML
70
56
  configuration files.
71
57
  email: remi@exomel.com
@@ -75,34 +61,31 @@ extensions: []
75
61
  extra_rdoc_files: []
76
62
  files:
77
63
  - ".gitignore"
64
+ - ".rubocop.yml"
78
65
  - ".travis.yml"
79
66
  - Gemfile
80
- - LICENSE
67
+ - LICENSE.md
81
68
  - README.md
82
69
  - Rakefile
83
70
  - bin/teamocil
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
89
71
  - lib/teamocil.rb
90
72
  - lib/teamocil/cli.rb
91
- - lib/teamocil/error.rb
73
+ - lib/teamocil/command/new_window.rb
74
+ - lib/teamocil/command/rename_session.rb
75
+ - lib/teamocil/command/rename_window.rb
76
+ - lib/teamocil/command/select_layout.rb
77
+ - lib/teamocil/command/select_pane.rb
78
+ - lib/teamocil/command/select_window.rb
79
+ - lib/teamocil/command/send_keys.rb
80
+ - lib/teamocil/command/send_keys_to_pane.rb
81
+ - lib/teamocil/command/split_window.rb
92
82
  - lib/teamocil/layout.rb
93
- - lib/teamocil/layout/pane.rb
94
- - lib/teamocil/layout/session.rb
95
- - lib/teamocil/layout/window.rb
83
+ - lib/teamocil/tmux/pane.rb
84
+ - lib/teamocil/tmux/session.rb
85
+ - lib/teamocil/tmux/window.rb
86
+ - lib/teamocil/utils/closed_struct.rb
87
+ - lib/teamocil/utils/option_parser.rb
96
88
  - lib/teamocil/version.rb
97
- - spec/cli_spec.rb
98
- - spec/fixtures/.my-fancy-layouts-directory/sample-3.yml
99
- - spec/fixtures/.teamocil/sample-2.yml
100
- - spec/fixtures/.teamocil/sample.yml
101
- - spec/fixtures/layouts.yml
102
- - spec/layout_spec.rb
103
- - spec/mock/cli.rb
104
- - spec/mock/layout.rb
105
- - spec/spec_helper.rb
106
89
  - teamocil.gemspec
107
90
  homepage: http://remiprev.github.com/teamocil
108
91
  licenses:
@@ -128,14 +111,4 @@ rubygems_version: 2.2.2
128
111
  signing_key:
129
112
  specification_version: 4
130
113
  summary: Easy session, window and pane layouts for tmux
131
- test_files:
132
- - spec/cli_spec.rb
133
- - spec/fixtures/.my-fancy-layouts-directory/sample-3.yml
134
- - spec/fixtures/.teamocil/sample-2.yml
135
- - spec/fixtures/.teamocil/sample.yml
136
- - spec/fixtures/layouts.yml
137
- - spec/layout_spec.rb
138
- - spec/mock/cli.rb
139
- - spec/mock/layout.rb
140
- - spec/spec_helper.rb
141
- has_rdoc:
114
+ test_files: []
@@ -1,8 +0,0 @@
1
- windows:
2
- - name: simple-four-panes
3
- layout: tiled
4
- panes:
5
- - cmd: echo 'first pane'
6
- - cmd: echo 'second pane'
7
- - cmd: echo 'fourth pane'
8
- - cmd: echo 'third pane'
@@ -1,8 +0,0 @@
1
- windows:
2
- - name: simple-one-and-three-panes
3
- layout: main-vertical
4
- panes:
5
- - cmd: echo 'first pane'
6
- - cmd: echo 'second pane'
7
- - cmd: echo 'third pane'
8
- - cmd: echo 'fourth pane'
@@ -1,10 +0,0 @@
1
- windows:
2
- - name: simple-six-panes
3
- layout: tiled
4
- panes:
5
- - cmd: echo 'first pane'
6
- - cmd: echo 'second pane'
7
- - cmd: echo 'fourth pane'
8
- - cmd: echo 'third pane'
9
- - cmd: echo 'sixth pane'
10
- - cmd: echo 'fifth pane'
@@ -1,6 +0,0 @@
1
- windows:
2
- - name: simple-two-vertical-panes
3
- layout: even-horizontal
4
- panes:
5
- - cmd: echo 'first pane'
6
- - cmd: echo 'second pane'
@@ -1,6 +0,0 @@
1
- windows:
2
- - name: simple-two-horizontal-panes
3
- layout: even-vertical
4
- panes:
5
- - cmd: echo 'first pane'
6
- - cmd: echo 'second pane'
@@ -1,6 +0,0 @@
1
- module Teamocil
2
- module Error
3
- class StandardError < ::StandardError; end
4
- class LayoutError < StandardError; end
5
- end
6
- end
@@ -1,66 +0,0 @@
1
- module Teamocil
2
- class Layout
3
- # This class represents a pane within a tmux window
4
- class Pane
5
- attr_reader :width, :height, :cmd, :index, :target, :focus
6
-
7
- # Initialize a new tmux pane
8
- #
9
- # @param session [Session] the window where the pane is initialized
10
- # @param index [Fixnnum] the pane index
11
- # @param attrs [Hash] the pane data from the layout file
12
- def initialize(window, index, attrs={})
13
- raise Teamocil::Error::LayoutError.new("You cannot have empty panes") if attrs.nil?
14
- @height = attrs["height"]
15
- @width = attrs["width"]
16
- @cmd = attrs["cmd"]
17
- @target = attrs["target"]
18
- @focus = attrs["focus"] || false
19
-
20
- @window = window
21
- @index = index
22
- end
23
-
24
- # Generate commands to send to tmux
25
- #
26
- # @return [Array]
27
- def generate_commands
28
- commands = []
29
-
30
- # Is it a vertical or horizontal pane
31
- init_command = ""
32
- unless @index == @window.pane_base_index
33
- if !@width.nil?
34
- init_command = "tmux split-window -h -p #{@width}"
35
- elsif !@height.nil?
36
- init_command = "tmux split-window -p #{@height}"
37
- else
38
- init_command = "tmux split-window"
39
- end
40
- init_command << " -t #{@target}" unless @target.nil?
41
- commands << init_command
42
- end
43
-
44
- # Wrap all commands around filters
45
- @cmd = [@window.filters["before"]] + [@window.clear] + [@cmd] + [@window.filters["after"]]
46
-
47
- # If a `root` key exist, start each pane in this directory
48
- @cmd.unshift "cd \"#{@window.root}\"" unless @window.root.nil?
49
-
50
- # Set the TEAMOCIL environment variable
51
- # depending on the shell set in ENV
52
- if ENV['SHELL'].scan(/fish/).empty?
53
- @cmd.unshift "export TEAMOCIL=1" if @window.with_env_var
54
- else
55
- @cmd.unshift "set -gx TEAMOCIL 1"
56
- end
57
-
58
- # Execute each pane command
59
- commands << "tmux send-keys -t #{@index} \"#{@cmd.flatten.compact.join(@window.cmd_separator)}\""
60
- commands << "tmux send-keys -t #{@index} Enter"
61
-
62
- commands
63
- end
64
- end
65
- end
66
- end