automux 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +20 -0
- data/LICENSE.txt +22 -0
- data/VERSION +1 -0
- data/automux.gemspec +18 -0
- data/bin/automux +22 -0
- data/data/automux/blueprints/default.yml +26 -0
- data/data/automux/recipes/default.sh.erb +46 -0
- data/features/custom_recipes.feature +63 -0
- data/features/error_messages.feature +8 -0
- data/features/general_usage.feature +180 -0
- data/features/managing_blueprints.feature +66 -0
- data/features/runtime_options.feature +84 -0
- data/features/session_and_window_options.feature +67 -0
- data/features/session_hooks.feature +68 -0
- data/features/setup_windows.feature +22 -0
- data/features/step_definitions/custom_recipes_steps.rb +9 -0
- data/features/step_definitions/managing_blueprints_steps.rb +9 -0
- data/features/step_definitions/setup_windows_steps.rb +18 -0
- data/features/step_definitions/shared_steps.rb +43 -0
- data/features/support/env.rb +8 -0
- data/features/support/hooks.rb +4 -0
- data/features/support/transformations.rb +6 -0
- data/lib/automux.rb +9 -0
- data/lib/automux/cache.rb +10 -0
- data/lib/automux/cache/blueprint.rb +20 -0
- data/lib/automux/cache/recipe.rb +27 -0
- data/lib/automux/controller.rb +14 -0
- data/lib/automux/controller/base.rb +18 -0
- data/lib/automux/controller/blueprints.rb +45 -0
- data/lib/automux/controller/messages.rb +11 -0
- data/lib/automux/controller/recipes.rb +39 -0
- data/lib/automux/controller/setup.rb +11 -0
- data/lib/automux/controller/support.rb +5 -0
- data/lib/automux/controller/support/filters.rb +55 -0
- data/lib/automux/controller/support/rendering.rb +54 -0
- data/lib/automux/core.rb +11 -0
- data/lib/automux/core/base.rb +7 -0
- data/lib/automux/core/blueprint.rb +31 -0
- data/lib/automux/core/error.rb +15 -0
- data/lib/automux/core/hook.rb +26 -0
- data/lib/automux/core/option.rb +20 -0
- data/lib/automux/core/recipe.rb +12 -0
- data/lib/automux/core/support.rb +6 -0
- data/lib/automux/core/support/custom_accessors.rb +15 -0
- data/lib/automux/core/support/hooks_helper.rb +28 -0
- data/lib/automux/core/support/options_helper.rb +18 -0
- data/lib/automux/core/tmux.rb +6 -0
- data/lib/automux/core/tmux/pane.rb +19 -0
- data/lib/automux/core/tmux/session.rb +137 -0
- data/lib/automux/core/tmux/window.rb +76 -0
- data/lib/automux/initializers.rb +2 -0
- data/lib/automux/initializers/custom_hooks.rb +4 -0
- data/lib/automux/initializers/setup_caches.rb +3 -0
- data/lib/automux/library.rb +5 -0
- data/lib/automux/library/mini_erb.rb +22 -0
- data/lib/automux/library/yaml_parser.rb +41 -0
- data/lib/automux/paths.rb +41 -0
- data/lib/automux/setup.rb +0 -0
- data/lib/automux/views/blueprints/copy.sh.erb +2 -0
- data/lib/automux/views/blueprints/create.sh.erb +2 -0
- data/lib/automux/views/blueprints/edit.sh.erb +1 -0
- data/lib/automux/views/messages/error.sh.erb +3 -0
- data/lib/automux/views/setup/clone_defaults.sh.erb +21 -0
- data/test/session_test.rb +16 -0
- data/test/support/common_methods.rb +0 -0
- data/test/support/factories.rb +13 -0
- data/test/support/hash_factory.rb +19 -0
- data/test/support/test_helper.rb +7 -0
- metadata +136 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
module Automux
|
2
|
+
module Core
|
3
|
+
module Tmux
|
4
|
+
class Pane < Base
|
5
|
+
attr_reader :command, :window
|
6
|
+
private :window
|
7
|
+
|
8
|
+
def initialize(window, command)
|
9
|
+
@window = window
|
10
|
+
@command = command
|
11
|
+
end
|
12
|
+
|
13
|
+
def index
|
14
|
+
window.panes.find_index(self)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
module Automux
|
2
|
+
module Core
|
3
|
+
module Tmux
|
4
|
+
class Session < Base
|
5
|
+
include Support::HooksHelper
|
6
|
+
include Support::OptionsHelper
|
7
|
+
|
8
|
+
attr_reader :data, :name, :root, :data_windows, :flags, :base_index
|
9
|
+
dup_attr_reader :windows, :hooks, :options
|
10
|
+
private :data, :data_windows
|
11
|
+
|
12
|
+
def initialize(blueprint_data)
|
13
|
+
@data = blueprint_data
|
14
|
+
@name = data['name']
|
15
|
+
@root = data['root'] || '.'
|
16
|
+
@data_windows = data['windows'] || []
|
17
|
+
@data_hooks = data['hooks'] || []
|
18
|
+
@windows = []
|
19
|
+
@flags = data['flags']
|
20
|
+
@hooks = []
|
21
|
+
@data_options = data['options'] || []
|
22
|
+
@options = []
|
23
|
+
end
|
24
|
+
|
25
|
+
def start_server
|
26
|
+
%[tmux start-server]
|
27
|
+
end
|
28
|
+
|
29
|
+
def new_session
|
30
|
+
%[#{ tmux_with_flags } new-session -d -s #{ name }]
|
31
|
+
end
|
32
|
+
|
33
|
+
def set_option(option)
|
34
|
+
%[tmux set-option #{ option.name } '#{ option.value }']
|
35
|
+
end
|
36
|
+
|
37
|
+
def new_window(window)
|
38
|
+
%[tmux new-window -t #{ name }:#{ window.index } 2> /dev/null]
|
39
|
+
end
|
40
|
+
|
41
|
+
def rename_window(window, window_name = window.name)
|
42
|
+
%[tmux rename-window -t #{ name }:#{ window.index } #{ window_name }]
|
43
|
+
end
|
44
|
+
|
45
|
+
def send_keys(identifier, command)
|
46
|
+
window = get_window(identifier)
|
47
|
+
%[tmux send-keys -t #{ name }:#{ window.index } "#{ command }" C-m]
|
48
|
+
end
|
49
|
+
|
50
|
+
def attach_session
|
51
|
+
%[#{ tmux_with_flags } attach-session -t #{ name }]
|
52
|
+
end
|
53
|
+
|
54
|
+
def select_layout(window)
|
55
|
+
%[tmux select-layout -t #{ name }:#{ window.index } #{ window.layout }]
|
56
|
+
end
|
57
|
+
|
58
|
+
def create_pane
|
59
|
+
%[tmux split-window]
|
60
|
+
end
|
61
|
+
|
62
|
+
def get_binding
|
63
|
+
binding
|
64
|
+
end
|
65
|
+
|
66
|
+
def setup
|
67
|
+
setup_options
|
68
|
+
setup_base_index
|
69
|
+
setup_windows
|
70
|
+
setup_hooks
|
71
|
+
end
|
72
|
+
|
73
|
+
def window_indexes
|
74
|
+
@windows.map(&:index).compact
|
75
|
+
end
|
76
|
+
|
77
|
+
def next_available_window_index
|
78
|
+
n = base_index + number_of_windows
|
79
|
+
(Array(base_index..n) - window_indexes).first
|
80
|
+
end
|
81
|
+
|
82
|
+
private ###
|
83
|
+
|
84
|
+
def tmux_with_flags
|
85
|
+
%[tmux #{ flags }].strip
|
86
|
+
end
|
87
|
+
|
88
|
+
def number_of_windows
|
89
|
+
@windows.length
|
90
|
+
end
|
91
|
+
|
92
|
+
def add_window(window)
|
93
|
+
@windows << window
|
94
|
+
end
|
95
|
+
|
96
|
+
# When multiple windows have been assigned the same index by the user, the additional window indexes will be removed.
|
97
|
+
def remove_duplicate_indexes(original_data)
|
98
|
+
non_indexed_data = original_data.select { |h| h['index'].to_s.empty? }
|
99
|
+
indexed_data = original_data - non_indexed_data
|
100
|
+
|
101
|
+
uniq_indexed_data = indexed_data.uniq { |h| h['index'] }
|
102
|
+
conflicting_data = indexed_data - uniq_indexed_data
|
103
|
+
removed_index_data = conflicting_data.each { |h| h.delete('index') }
|
104
|
+
|
105
|
+
non_indexed_data + uniq_indexed_data + removed_index_data
|
106
|
+
end
|
107
|
+
|
108
|
+
def add_windows(windows_data)
|
109
|
+
windows_data.each do |window_data|
|
110
|
+
window = Automux::Core::Tmux::Window.new(self, window_data)
|
111
|
+
add_window(window) if window.opted_in?
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def setup_windows
|
116
|
+
windows_data = remove_duplicate_indexes(data_windows)
|
117
|
+
add_windows(windows_data)
|
118
|
+
@windows.each(&:update_index)
|
119
|
+
@windows.each(&:setup)
|
120
|
+
end
|
121
|
+
|
122
|
+
def setup_base_index
|
123
|
+
@base_index = 0
|
124
|
+
if option = options.find { |option| option.name == 'base-index' }
|
125
|
+
@base_index = option.value.to_i
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def get_window(identifier)
|
130
|
+
return identifier if identifier.is_a?(Window)
|
131
|
+
|
132
|
+
@windows.find { |window| [window.index, window.name].include?(identifier) }
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Automux
|
2
|
+
module Core
|
3
|
+
module Tmux
|
4
|
+
class Window < Base
|
5
|
+
include Support::HooksHelper
|
6
|
+
include Support::OptionsHelper
|
7
|
+
|
8
|
+
attr_reader :data, :session, :index, :root
|
9
|
+
dup_attr_reader :panes, :hooks, :options
|
10
|
+
private :data, :session
|
11
|
+
|
12
|
+
def initialize(session, data)
|
13
|
+
@session = session
|
14
|
+
@data = data
|
15
|
+
@opt = data['opt']
|
16
|
+
@index = data['index']
|
17
|
+
@root = data['root']
|
18
|
+
@data_hooks = data['hooks'] || []
|
19
|
+
@hooks = []
|
20
|
+
@data_options = data['options'] || []
|
21
|
+
@options = []
|
22
|
+
@panes = []
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_option(option)
|
26
|
+
%[tmux set-window-option -t #{ session.name }:#{ index } #{ option.name } '#{ option.value }']
|
27
|
+
end
|
28
|
+
|
29
|
+
def name
|
30
|
+
data['name']
|
31
|
+
end
|
32
|
+
|
33
|
+
def setup
|
34
|
+
setup_options
|
35
|
+
setup_panes
|
36
|
+
setup_hooks
|
37
|
+
end
|
38
|
+
|
39
|
+
def has_panes?
|
40
|
+
!@panes.nil?
|
41
|
+
end
|
42
|
+
|
43
|
+
def layout
|
44
|
+
data['layout']
|
45
|
+
end
|
46
|
+
|
47
|
+
def update_index
|
48
|
+
@index ||= session.next_available_window_index
|
49
|
+
end
|
50
|
+
|
51
|
+
def opted_in?
|
52
|
+
return true if @opt.nil?
|
53
|
+
|
54
|
+
@opt
|
55
|
+
end
|
56
|
+
|
57
|
+
def change_root_command
|
58
|
+
%[cd #{ root }]
|
59
|
+
end
|
60
|
+
|
61
|
+
def get_binding
|
62
|
+
binding
|
63
|
+
end
|
64
|
+
|
65
|
+
private ###
|
66
|
+
|
67
|
+
def setup_panes
|
68
|
+
[data['panes']].flatten.each do |command|
|
69
|
+
pane = Automux::Core::Tmux::Pane.new(self, command)
|
70
|
+
@panes << pane
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
module Automux
|
4
|
+
module Library
|
5
|
+
class MiniErb < ERB
|
6
|
+
|
7
|
+
def initialize(template)
|
8
|
+
modified_template = erb_parseable_template(template)
|
9
|
+
super(modified_template, nil, '%<>')
|
10
|
+
end
|
11
|
+
|
12
|
+
private ###
|
13
|
+
|
14
|
+
# - [1, 2, 3] => % [1, 2, 3]
|
15
|
+
# = [1, 2, 3] => <%= [1, 2, 3] %>
|
16
|
+
def erb_parseable_template(string)
|
17
|
+
string.gsub(/^\s*-(.+)$/, '%\1').
|
18
|
+
gsub(/^\s*=(.+)$/, '<%=\1 %> ')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'optparse'
|
3
|
+
|
4
|
+
module Automux
|
5
|
+
module Library
|
6
|
+
class YamlParser
|
7
|
+
class << self
|
8
|
+
def load_file(file)
|
9
|
+
data_string = File.read(file)
|
10
|
+
opts_replaced_string = replace_opts_with_user_input(data_string)
|
11
|
+
YAML.load(opts_replaced_string)
|
12
|
+
end
|
13
|
+
|
14
|
+
private ###
|
15
|
+
|
16
|
+
# Scan blueprint for patterns like "-\w" or '-\w' to get a options list.
|
17
|
+
# The options list is used by OptionsParser to read the options from commandline.
|
18
|
+
def replace_opts_with_user_input(string)
|
19
|
+
blueprint_opts = string.scan(/['|"]-(\w:?)['|"]/m).flatten
|
20
|
+
user_input_values = get_options(blueprint_opts)
|
21
|
+
replace_opts(string, user_input_values)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Replace patterns like "-\w" with matching commandline option.
|
25
|
+
def replace_opts(string, values)
|
26
|
+
values.each do |k, v|
|
27
|
+
# Interpolate #{ v } - to substitute literal booleans.
|
28
|
+
string.gsub!(/['|"]-(#{ k }):?['|"]/m, "#{ v }")
|
29
|
+
end
|
30
|
+
|
31
|
+
string
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_options(opts)
|
35
|
+
ARGV.shift while ARGV[0].to_s.match(/^\w/)
|
36
|
+
ARGV.getopts(*opts)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Automux
|
2
|
+
module Paths
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def default_blueprint
|
6
|
+
File.join(data, 'blueprints', 'default.yml')
|
7
|
+
end
|
8
|
+
|
9
|
+
def default_recipe
|
10
|
+
File.join(data, 'recipes', 'default.sh.erb')
|
11
|
+
end
|
12
|
+
|
13
|
+
def data
|
14
|
+
File.join(root, 'data/automux')
|
15
|
+
end
|
16
|
+
|
17
|
+
def root
|
18
|
+
File.expand_path('../../../', __FILE__)
|
19
|
+
end
|
20
|
+
|
21
|
+
def views
|
22
|
+
'lib/automux/views'
|
23
|
+
end
|
24
|
+
|
25
|
+
def user_assets
|
26
|
+
File.join(ENV['HOME'], '.automux')
|
27
|
+
end
|
28
|
+
|
29
|
+
%w(blueprints recipes).each do |name|
|
30
|
+
define_method(name) do
|
31
|
+
Dir[File.join(user_assets, name, '*')]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
%w(blueprints recipes).each do |name|
|
36
|
+
define_method("#{ name }_container") do
|
37
|
+
File.join(user_assets, name)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
File without changes
|
@@ -0,0 +1 @@
|
|
1
|
+
$EDITOR <%= path %>
|
@@ -0,0 +1,21 @@
|
|
1
|
+
setup() {
|
2
|
+
mkdir -p <%= Automux::Paths.blueprints_container %>
|
3
|
+
mkdir -p <%= Automux::Paths.recipes_container %>
|
4
|
+
|
5
|
+
cp <%= Automux::Paths.default_blueprint %> <%= Automux::Paths.blueprints_container %>
|
6
|
+
cp <%= Automux::Paths.default_recipe %> <%= Automux::Paths.recipes_container %>
|
7
|
+
|
8
|
+
echo default files copied
|
9
|
+
}
|
10
|
+
|
11
|
+
if [ -d <%= Automux::Paths.blueprints_container %> ]; then
|
12
|
+
read -r -p "This will replace the default blueprint and recipe under ~/.automux. Continue? [Y/n] " response
|
13
|
+
|
14
|
+
case $response in
|
15
|
+
[yY][eE][sS]|[yY])
|
16
|
+
setup
|
17
|
+
;;
|
18
|
+
esac
|
19
|
+
else
|
20
|
+
setup
|
21
|
+
fi
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'support/test_helper'
|
2
|
+
|
3
|
+
describe 'Session' do
|
4
|
+
it "should assign window indexes" do
|
5
|
+
|
6
|
+
windows_data = [nil, nil, 1, 0].map do |i|
|
7
|
+
HashFactory.build(:window, index: i)
|
8
|
+
end
|
9
|
+
data = HashFactory.build(:session, windows: windows_data)
|
10
|
+
|
11
|
+
session = Automux::Core::Tmux::Session.new(data)
|
12
|
+
session.setup_windows_and_hooks
|
13
|
+
|
14
|
+
session.window_indexes.must_equal [2, 3, 1, 0]
|
15
|
+
end
|
16
|
+
end
|
File without changes
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class HashFactory
|
2
|
+
@@factories = {}
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def define(name, &block)
|
6
|
+
@@factories[name.to_s] = block
|
7
|
+
end
|
8
|
+
|
9
|
+
def build(name, attrs = {})
|
10
|
+
defaults = stringify(@@factories[name.to_s].call)
|
11
|
+
attributes = stringify(attrs)
|
12
|
+
defaults.merge(attributes)
|
13
|
+
end
|
14
|
+
|
15
|
+
def stringify(data)
|
16
|
+
Hash[data.map { |k, v| [k.to_s, v] }]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|