teamocil 0.4.5 → 1.0
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/.gitignore +14 -4
- data/.rubocop.yml +48 -0
- data/.travis.yml +2 -2
- data/Gemfile +1 -1
- data/{LICENSE → LICENSE.md} +0 -0
- data/README.md +124 -166
- data/Rakefile +12 -27
- data/bin/teamocil +3 -4
- data/lib/teamocil.rb +51 -4
- data/lib/teamocil/cli.rb +23 -90
- data/lib/teamocil/command/new_window.rb +16 -0
- data/lib/teamocil/command/rename_session.rb +9 -0
- data/lib/teamocil/command/rename_window.rb +9 -0
- data/lib/teamocil/command/select_layout.rb +9 -0
- data/lib/teamocil/command/select_pane.rb +9 -0
- data/lib/teamocil/command/select_window.rb +9 -0
- data/lib/teamocil/command/send_keys.rb +9 -0
- data/lib/teamocil/command/send_keys_to_pane.rb +9 -0
- data/lib/teamocil/command/split_window.rb +15 -0
- data/lib/teamocil/layout.rb +58 -36
- data/lib/teamocil/tmux/pane.rb +15 -0
- data/lib/teamocil/tmux/session.rb +28 -0
- data/lib/teamocil/tmux/window.rb +47 -0
- data/lib/teamocil/utils/closed_struct.rb +16 -0
- data/lib/teamocil/utils/option_parser.rb +56 -0
- data/lib/teamocil/version.rb +1 -1
- data/teamocil.gemspec +15 -16
- metadata +27 -54
- data/examples/four-splits.yml +0 -8
- data/examples/one-and-three-splits.yml +0 -8
- data/examples/six-splits.yml +0 -10
- data/examples/two-horizontal-splits.yml +0 -6
- data/examples/two-vertical-splits.yml +0 -6
- data/lib/teamocil/error.rb +0 -6
- data/lib/teamocil/layout/pane.rb +0 -66
- data/lib/teamocil/layout/session.rb +0 -30
- data/lib/teamocil/layout/window.rb +0 -77
- data/spec/cli_spec.rb +0 -79
- data/spec/fixtures/.my-fancy-layouts-directory/sample-3.yml +0 -10
- data/spec/fixtures/.teamocil/sample-2.yml +0 -10
- data/spec/fixtures/.teamocil/sample.yml +0 -10
- data/spec/fixtures/layouts.yml +0 -76
- data/spec/layout_spec.rb +0 -229
- data/spec/mock/cli.rb +0 -35
- data/spec/mock/layout.rb +0 -16
- data/spec/spec_helper.rb +0 -17
data/Rakefile
CHANGED
@@ -1,28 +1,13 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
14
|
-
|
15
|
-
desc "Generate YARD Documentation"
|
16
|
-
YARD::Rake::YardocTask.new do |task|
|
17
|
-
task.options = [
|
18
|
-
"-o", File.expand_path("../doc", __FILE__),
|
19
|
-
"--readme=README.md",
|
20
|
-
"--markup=markdown",
|
21
|
-
"--markup-provider=maruku",
|
22
|
-
"--no-private",
|
23
|
-
"--no-cache",
|
24
|
-
"--protected",
|
25
|
-
"--title=Teamocil",
|
26
|
-
]
|
27
|
-
task.files = ["lib/**/*.rb"]
|
1
|
+
require 'bundler'
|
2
|
+
require 'rake'
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
|
5
|
+
desc 'Start an IRB session with the gem'
|
6
|
+
task :console do
|
7
|
+
$LOAD_PATH.unshift File.expand_path('..', __FILE__)
|
8
|
+
require 'teamocil'
|
9
|
+
require 'irb'
|
10
|
+
|
11
|
+
ARGV.clear
|
12
|
+
IRB.start
|
28
13
|
end
|
data/bin/teamocil
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), *%w(.. lib))
|
2
3
|
|
3
|
-
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
4
|
-
|
5
|
-
require 'yaml'
|
6
4
|
require 'teamocil'
|
7
5
|
|
8
|
-
Teamocil::CLI.new(ARGV, ENV)
|
6
|
+
runner = Teamocil::CLI.new(arguments: ARGV, environment: ENV)
|
7
|
+
runner.run!
|
data/lib/teamocil.rb
CHANGED
@@ -1,7 +1,54 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
|
4
|
-
|
1
|
+
require 'yaml'
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
# Version
|
5
|
+
require 'teamocil/version'
|
6
|
+
|
7
|
+
# Utils
|
8
|
+
require 'teamocil/utils/closed_struct'
|
9
|
+
require 'teamocil/utils/option_parser'
|
10
|
+
|
11
|
+
# Teamocil
|
12
|
+
require 'teamocil/layout'
|
13
|
+
require 'teamocil/cli'
|
14
|
+
|
15
|
+
# Command classes
|
16
|
+
require 'teamocil/command/new_window'
|
17
|
+
require 'teamocil/command/rename_session'
|
18
|
+
require 'teamocil/command/rename_window'
|
19
|
+
require 'teamocil/command/select_layout'
|
20
|
+
require 'teamocil/command/select_pane'
|
21
|
+
require 'teamocil/command/select_window'
|
22
|
+
require 'teamocil/command/send_keys'
|
23
|
+
require 'teamocil/command/send_keys_to_pane'
|
24
|
+
require 'teamocil/command/split_window'
|
25
|
+
|
26
|
+
# Tmux classes
|
27
|
+
require 'teamocil/tmux/session'
|
28
|
+
require 'teamocil/tmux/window'
|
29
|
+
require 'teamocil/tmux/pane'
|
5
30
|
|
6
31
|
module Teamocil
|
32
|
+
class << self
|
33
|
+
attr_reader :options
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.bail(*args)
|
37
|
+
print '[teamocil error] '
|
38
|
+
puts(*args)
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.puts(*args)
|
43
|
+
STDOUT.puts(*args)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.system(*args)
|
47
|
+
Kernel.system(*args)
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.parse_options!(arguments:)
|
51
|
+
parser = OptionParser.new(arguments: arguments)
|
52
|
+
@options = parser.parsed_options
|
53
|
+
end
|
7
54
|
end
|
data/lib/teamocil/cli.rb
CHANGED
@@ -1,105 +1,38 @@
|
|
1
|
-
require 'optparse'
|
2
|
-
require 'fileutils'
|
3
|
-
require 'erb'
|
4
|
-
|
5
1
|
module Teamocil
|
6
|
-
|
7
|
-
|
8
|
-
attr_accessor :layout, :layouts
|
9
|
-
|
10
|
-
# Initialize a new run of `tmux`
|
11
|
-
#
|
12
|
-
# @param argv [Hash] the command line parameters hash (usually `ARGV`).
|
13
|
-
# @param env [Hash] the environment variables hash (usually `ENV`).
|
14
|
-
def initialize(argv, env)
|
15
|
-
parse_options! argv
|
16
|
-
layout_path = env["TEAMOCIL_PATH"] || File.join("#{env["HOME"]}", ".teamocil")
|
2
|
+
class CLI < ClosedStruct.new(:arguments, :environment)
|
3
|
+
DIRECTORY = '$HOME/.teamocil'
|
17
4
|
|
18
|
-
|
19
|
-
|
20
|
-
return print_layouts
|
21
|
-
end
|
22
|
-
|
23
|
-
if @options[:layout].nil? && argv[0].nil?
|
24
|
-
bail "You must supply a layout for teamocil to use. See `teamocil --help` for more options."
|
25
|
-
end
|
5
|
+
def run!
|
6
|
+
Teamocil.parse_options!(arguments: arguments)
|
26
7
|
|
27
|
-
|
8
|
+
# List available layouts
|
9
|
+
return Layout.print_available_layouts(directory: root) if Teamocil.options[:list]
|
28
10
|
|
29
|
-
|
30
|
-
|
31
|
-
Kernel.system("${EDITOR:-vim} \"#{file}\"")
|
32
|
-
elsif @options[:show]
|
33
|
-
::FileUtils.touch file unless File.exists?(file)
|
34
|
-
Kernel.system("cat \"#{file}\"")
|
35
|
-
else
|
36
|
-
bail "There is no file \"#{file}\"." unless File.exists?(file)
|
37
|
-
bail "You must be in a tmux session to use teamocil." unless env["TMUX"]
|
11
|
+
# Fetch the Layout object
|
12
|
+
layout = Layout.new(path: layout_file_path)
|
38
13
|
|
39
|
-
|
14
|
+
# Open layout file in $EDITOR
|
15
|
+
return layout.edit! if Teamocil.options[:edit]
|
40
16
|
|
41
|
-
|
17
|
+
# Output the layout raw content
|
18
|
+
return layout.show! if Teamocil.options[:show]
|
42
19
|
|
43
|
-
|
44
|
-
|
45
|
-
rescue Teamocil::Error::LayoutError => e
|
46
|
-
bail e.message
|
47
|
-
end
|
48
|
-
@layout.execute_commands(@layout.generate_commands)
|
49
|
-
end
|
20
|
+
# Nothing? Let’s execute this layout!
|
21
|
+
layout.execute!
|
50
22
|
end
|
51
23
|
|
52
|
-
|
53
|
-
def parse_options!(args)
|
54
|
-
@options = {}
|
55
|
-
opts = ::OptionParser.new do |opts|
|
56
|
-
opts.banner = "Usage: teamocil [options] <layout>
|
57
|
-
|
58
|
-
Options:
|
59
|
-
"
|
60
|
-
opts.on("--here", "Set up the first window in the current window") do
|
61
|
-
@options[:here] = true
|
62
|
-
end
|
63
|
-
|
64
|
-
opts.on("--edit", "Edit the YAML layout file instead of using it") do
|
65
|
-
@options[:edit] = true
|
66
|
-
end
|
67
|
-
|
68
|
-
opts.on("--layout [LAYOUT]", "Use a specific layout file, instead of `~/.teamocil/<layout>.yml`") do |layout|
|
69
|
-
@options[:layout] = layout
|
70
|
-
end
|
24
|
+
private
|
71
25
|
|
72
|
-
|
73
|
-
|
74
|
-
end
|
75
|
-
|
76
|
-
opts.on("--show", "Show the content of the layout file instead of executing it") do
|
77
|
-
@options[:show] = true
|
78
|
-
end
|
79
|
-
|
80
|
-
end
|
81
|
-
opts.parse! args
|
82
|
-
end
|
83
|
-
|
84
|
-
# Return an array of available layouts
|
85
|
-
#
|
86
|
-
# @param path [String] the path used to look for layouts
|
87
|
-
def get_layouts(path)
|
88
|
-
Dir.glob(File.join(path, "*.yml")).map { |file| File.basename(file).gsub(/\..+$/, "") }.sort
|
26
|
+
def root
|
27
|
+
DIRECTORY.sub('$HOME', environment['HOME'])
|
89
28
|
end
|
90
29
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
# Print an error message and exit the utility
|
98
|
-
#
|
99
|
-
# @param msg [Mixed] something to print before exiting.
|
100
|
-
def bail(msg)
|
101
|
-
STDERR.puts "[teamocil] #{msg}"
|
102
|
-
exit 1
|
30
|
+
def layout_file_path
|
31
|
+
if layout = Teamocil.options[:layout]
|
32
|
+
layout
|
33
|
+
else
|
34
|
+
File.join(root, "#{arguments.first}.yml")
|
35
|
+
end
|
103
36
|
end
|
104
37
|
end
|
105
38
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Teamocil
|
2
|
+
module Command
|
3
|
+
class NewWindow < ClosedStruct.new(:name, :root)
|
4
|
+
def to_s
|
5
|
+
"new-window #{options.join(' ')}"
|
6
|
+
end
|
7
|
+
|
8
|
+
def options
|
9
|
+
[].tap do |options|
|
10
|
+
options << "-n '#{name}'" if name
|
11
|
+
options << "-c '#{root}'" if root
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
data/lib/teamocil/layout.rb
CHANGED
@@ -1,44 +1,66 @@
|
|
1
|
-
require "teamocil/layout/session"
|
2
|
-
require "teamocil/layout/window"
|
3
|
-
require "teamocil/layout/pane"
|
4
|
-
|
5
1
|
module Teamocil
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
2
|
+
class Layout < ClosedStruct.new(:path, :options)
|
3
|
+
def execute!
|
4
|
+
if Teamocil.options[:debug]
|
5
|
+
Teamocil.puts(shell_commands.join("\n"))
|
6
|
+
else
|
7
|
+
Teamocil.system(shell_commands.join('; '))
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def show!
|
12
|
+
Teamocil.puts(raw_content)
|
13
|
+
end
|
14
|
+
|
15
|
+
def edit!
|
16
|
+
Teamocil.system("$EDITOR #{path}")
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.print_available_layouts(directory:)
|
20
|
+
files = Dir.glob(File.join(directory, '*.yml'))
|
21
|
+
|
22
|
+
files.map! do |file|
|
23
|
+
extname = File.extname(file)
|
24
|
+
File.basename(file).gsub(extname, '')
|
25
|
+
end
|
26
|
+
|
27
|
+
# Always return files in alphabetical order, even if `Dir.glob` almost
|
28
|
+
# always does it
|
29
|
+
files.sort!
|
30
|
+
|
31
|
+
Teamocil.puts(files)
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def shell_commands
|
37
|
+
commands = parsed_layout.as_tmux
|
38
|
+
commands.flatten.map { |command| "tmux #{command}" }
|
39
|
+
end
|
40
|
+
|
41
|
+
def parsed_layout
|
42
|
+
begin
|
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
|
+
Session.new(yaml_content)
|
32
50
|
else
|
33
|
-
|
51
|
+
Teamocil.bail("The layout at `#{path}` is not valid.")
|
34
52
|
end
|
35
53
|
end
|
36
54
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
55
|
+
def valid?
|
56
|
+
# TODO: Actually validate if the layout is valid
|
57
|
+
true
|
58
|
+
end
|
59
|
+
|
60
|
+
def raw_content
|
61
|
+
File.read(path)
|
62
|
+
rescue
|
63
|
+
Teamocil.bail("Cannot find a layout at `#{path}`")
|
42
64
|
end
|
43
65
|
end
|
44
66
|
end
|