heytmux 0.0.0 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +23 -2
- data/lib/heytmux.rb +5 -2
- data/lib/heytmux/core.rb +6 -21
- data/lib/heytmux/pane_action.rb +44 -0
- data/lib/heytmux/pane_actions/expect.rb +36 -0
- data/lib/heytmux/pane_actions/keys.rb +23 -0
- data/lib/heytmux/pane_actions/paste.rb +21 -0
- data/lib/heytmux/pane_actions/sleep.rb +18 -0
- data/lib/heytmux/validations.rb +20 -19
- data/lib/heytmux/version.rb +1 -1
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53af5daf1e2193c29b0a85affdb4fc72b739b536
|
4
|
+
data.tar.gz: 76fe05372f8e0252556ae26cbcb98f40579dca4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 80b1a5981d257825a5ac9478c2e0dc089f4898a2ad17e49eb78d189227f3e46348fefe196b1b79b061237a28f1ee6730a5ccb5c22f7bf536d7f22cd37d64a0e8
|
7
|
+
data.tar.gz: b19c74aa98d105879a6374e9d92639906338d0dca952055c07d1af8f974421a69894d4d82e97c46136a68b8a6c209e4b107fddb2c4715e28655e16b752147529
|
data/README.md
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
Hey tmux!
|
2
2
|
=========
|
3
3
|
|
4
|
+
[![travis-ci](https://travis-ci.org/junegunn/heytmux.svg?branch=master)](https://travis-ci.org/junegunn/heytmux) [![Coverage Status](https://coveralls.io/repos/github/junegunn/heytmux/badge.svg?branch=master)](https://coveralls.io/github/junegunn/heytmux?branch=master)
|
5
|
+
|
4
6
|
Tmux scripting made easy.
|
5
7
|
|
6
8
|
Installation
|
@@ -25,7 +27,9 @@ Plug 'junegunn/heytmux'
|
|
25
27
|
```
|
26
28
|
|
27
29
|
- Registers `:Heytmux` command
|
28
|
-
- No need to install Gem
|
30
|
+
- No need to install Gem if you only use Heytmux inside Vim
|
31
|
+
- But if you want it to be globally available,
|
32
|
+
`Plug 'junegunn/heytmux', { 'do': 'gem install heytmux' }`
|
29
33
|
|
30
34
|
Usage
|
31
35
|
-----
|
@@ -252,7 +256,7 @@ following example will not work as expected.
|
|
252
256
|
```
|
253
257
|
|
254
258
|
With `expect` construct, you can make Heytmux wait until a certain regular
|
255
|
-
expression pattern appears on the
|
259
|
+
expression pattern appears on the pane (a la [Expect][expect]).
|
256
260
|
|
257
261
|
[expect]: https://en.wikipedia.org/wiki/Expect
|
258
262
|
|
@@ -265,6 +269,23 @@ expression pattern appears on the the pane (a la [Expect][expect]).
|
|
265
269
|
- uptime
|
266
270
|
```
|
267
271
|
|
272
|
+
#### Special commands
|
273
|
+
|
274
|
+
In addition to `expect`, Heytmux also supports `sleep` and `keys` commands.
|
275
|
+
`sleep` suspends the execution for a given time period. It's useful when the
|
276
|
+
shell on the target pane is non-interactive so you can't send `sleep` command
|
277
|
+
to it. `keys` command is for sending special keys, such as `c-c` (CTRL-C)
|
278
|
+
using `tmux send-keys` command. To send multiple keys, specify the keys as
|
279
|
+
a YAML list (e.g. `[c-c, c-l]`).
|
280
|
+
|
281
|
+
```yaml
|
282
|
+
- servers:
|
283
|
+
- server 1:
|
284
|
+
- vmstat 2 | tee log
|
285
|
+
- sleep: 3
|
286
|
+
- keys: c-c
|
287
|
+
```
|
288
|
+
|
268
289
|
Vim plugin
|
269
290
|
----------
|
270
291
|
|
data/lib/heytmux.rb
CHANGED
@@ -13,8 +13,6 @@ module Heytmux
|
|
13
13
|
'pane-border-format' => '#{pane_title}'
|
14
14
|
}.freeze
|
15
15
|
|
16
|
-
SUPPORTED_ACTIONS = %w[expect].to_set.freeze
|
17
|
-
|
18
16
|
WINDOWS_KEY = 'windows'
|
19
17
|
LAYOUT_KEY = 'layout'
|
20
18
|
PANES_KEY = 'panes'
|
@@ -28,5 +26,10 @@ end
|
|
28
26
|
|
29
27
|
require 'heytmux/version'
|
30
28
|
require 'heytmux/validations'
|
29
|
+
require 'heytmux/pane_action'
|
30
|
+
require 'heytmux/pane_actions/paste'
|
31
|
+
require 'heytmux/pane_actions/expect'
|
32
|
+
require 'heytmux/pane_actions/sleep'
|
33
|
+
require 'heytmux/pane_actions/keys'
|
31
34
|
require 'heytmux/tmux'
|
32
35
|
require 'heytmux/core'
|
data/lib/heytmux/core.rb
CHANGED
@@ -69,33 +69,18 @@ module Heytmux
|
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
72
|
+
# Finds appropriate PaneActions for the commands and processes them
|
72
73
|
def process_command!(window_index, pane)
|
73
74
|
pane_index, item, commands = pane.values_at(:index, :item, :command)
|
74
|
-
[*commands].each do |command|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
wait_until do
|
80
|
-
content = Tmux.capture(window_index, pane_index)
|
81
|
-
content =~ regex
|
82
|
-
end
|
83
|
-
else
|
84
|
-
command = command.gsub(ITEM_PAT, item) if item
|
85
|
-
Tmux.paste(window_index, pane_index, command)
|
75
|
+
[*commands].compact.each do |command|
|
76
|
+
label, body = command.is_a?(Hash) ? command.first : [:paste, command]
|
77
|
+
PaneAction.for(label).process(window_index, pane_index, body) do |source|
|
78
|
+
string = source.to_s
|
79
|
+
item ? string.gsub(ITEM_PAT, item) : string
|
86
80
|
end
|
87
81
|
end
|
88
82
|
end
|
89
83
|
|
90
|
-
def wait_until
|
91
|
-
timeout = Time.now + EXPECT_TIMEOUT
|
92
|
-
loop do
|
93
|
-
sleep EXPECT_SLEEP_INTERVAL
|
94
|
-
return if yield
|
95
|
-
raise 'Timed out' if Time.now > timeout
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
84
|
# Groups entities by the group key and extracts unique, sorted indexes and
|
100
85
|
# returns stateful indexer that issues index number for the given group key
|
101
86
|
def indexer(entities, group_key, index_key)
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Heytmux
|
4
|
+
# Base class for pane actions. To implement a new type of action that is
|
5
|
+
# applied to a designated pane, one should write a class that derives from
|
6
|
+
# this class. Use register class method to associate the class with one or
|
7
|
+
# more labels.
|
8
|
+
class PaneAction
|
9
|
+
# Validation method to be overridden. Throw ArgumentError if body is
|
10
|
+
# invalid.
|
11
|
+
def validate(_body)
|
12
|
+
nil
|
13
|
+
end
|
14
|
+
|
15
|
+
# Defines the behavior of the action. Should be implemented by the
|
16
|
+
# subclasses. When the method is called, a block is passed that is for
|
17
|
+
# replacing {{ item }} in the body. One may or may not want to use it on
|
18
|
+
# body depending on the use case.
|
19
|
+
def process(_window_index, _pane_index, _body)
|
20
|
+
raise NotImplementedError
|
21
|
+
end
|
22
|
+
|
23
|
+
class << self
|
24
|
+
# Registers a PaneAction class with the label
|
25
|
+
def register(*labels, klass)
|
26
|
+
instance = klass.new
|
27
|
+
(@actions ||= {}).merge!(
|
28
|
+
Hash[labels.map { |label| [label, instance] }]
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Finds PaneAction class for the label
|
33
|
+
def for(label)
|
34
|
+
@actions[label.to_sym]
|
35
|
+
end
|
36
|
+
|
37
|
+
def inherited(klass)
|
38
|
+
def klass.register(*labels)
|
39
|
+
PaneAction.register(*labels, self)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'English'
|
4
|
+
|
5
|
+
module Heytmux
|
6
|
+
module PaneActions
|
7
|
+
# Pastes the command onto the pane
|
8
|
+
class Expect < PaneAction
|
9
|
+
register :expect
|
10
|
+
|
11
|
+
def validate(body)
|
12
|
+
raise ArgumentError, 'Expect condition is empty' if body.to_s.empty?
|
13
|
+
end
|
14
|
+
|
15
|
+
def process(window_index, pane_index, body)
|
16
|
+
regex = Regexp.compile(yield(body))
|
17
|
+
wait_until do
|
18
|
+
content = Tmux.capture(window_index, pane_index)
|
19
|
+
raise 'Failed to capture pane content' unless $CHILD_STATUS.success?
|
20
|
+
content =~ regex
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def wait_until
|
27
|
+
timeout = Time.now + EXPECT_TIMEOUT
|
28
|
+
loop do
|
29
|
+
sleep EXPECT_SLEEP_INTERVAL
|
30
|
+
return if yield
|
31
|
+
raise 'Timed out' if Time.now > timeout
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Heytmux
|
4
|
+
module PaneActions
|
5
|
+
# Send keys using tmux send-keys command
|
6
|
+
class Keys < PaneAction
|
7
|
+
register :keys, :key
|
8
|
+
|
9
|
+
def validate(body)
|
10
|
+
body = body.is_a?(Array) ? body : [body]
|
11
|
+
body.each do |key|
|
12
|
+
unless Validations.valid_string?(key)
|
13
|
+
raise ArgumentError, 'Keys must be given as a string or a list'
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def process(window_index, pane_index, body)
|
19
|
+
Tmux.tmux('send-keys', Tmux.target(window_index, pane_index), body)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Heytmux
|
4
|
+
module PaneActions
|
5
|
+
# Waits for a certain regular expression pattern appears
|
6
|
+
class Paste < PaneAction
|
7
|
+
register :paste
|
8
|
+
|
9
|
+
def validate(body)
|
10
|
+
case body
|
11
|
+
when Hash, Array
|
12
|
+
raise ArgumentError, "Invalid command: #{body}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def process(window_index, pane_index, body)
|
17
|
+
Tmux.paste(window_index, pane_index, yield(body))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Heytmux
|
4
|
+
module PaneActions
|
5
|
+
# Sleeps for the given duration
|
6
|
+
class Sleep < PaneAction
|
7
|
+
register :sleep
|
8
|
+
|
9
|
+
def validate(body)
|
10
|
+
raise ArgumentError, 'Sleep expects positive number' if body.to_f <= 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def process(_window_index, _pane_index, body)
|
14
|
+
sleep(body.to_f)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/heytmux/validations.rb
CHANGED
@@ -65,29 +65,28 @@ module Heytmux
|
|
65
65
|
# - command_string
|
66
66
|
# - [command_strings_or_hashes...]
|
67
67
|
def validate_commands(commands)
|
68
|
-
return if commands.is_a?(String)
|
69
68
|
message = "Invalid command: #{commands.inspect}"
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
69
|
+
commands = commands.is_a?(Array) ? commands : [commands]
|
70
|
+
commands.each do |command|
|
71
|
+
case command
|
72
|
+
when Hash
|
74
73
|
raise ArgumentError, message unless single_spec?(command)
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
raise ArgumentError, message unless valid_name?(condition)
|
80
|
-
Regexp.compile(condition.to_s)
|
74
|
+
label, body = command.first
|
75
|
+
validate_action(label, body)
|
76
|
+
else
|
77
|
+
raise ArgumentError, message unless valid_string?(command)
|
81
78
|
end
|
82
|
-
strings.each do |command|
|
83
|
-
raise ArgumentError, message unless valid_name?(command)
|
84
|
-
end
|
85
|
-
else
|
86
|
-
raise ArgumentError, message unless valid_name?(commands)
|
87
79
|
end
|
88
80
|
nil
|
89
81
|
end
|
90
82
|
|
83
|
+
# Checks if the action is currently supported
|
84
|
+
def validate_action(label, body)
|
85
|
+
action = PaneAction.for(label)
|
86
|
+
raise ArgumentError, "Unsupported action: #{label}" unless action
|
87
|
+
action.validate(body)
|
88
|
+
end
|
89
|
+
|
91
90
|
# Checks if the given hash only contains one mapping from a string to
|
92
91
|
# a hash
|
93
92
|
def single_spec?(spec, *klasses)
|
@@ -95,11 +94,13 @@ module Heytmux
|
|
95
94
|
second.nil? && !key.to_s.empty? &&
|
96
95
|
(klasses.empty? || klasses.any? { |k| value.is_a?(k) })
|
97
96
|
end
|
98
|
-
|
97
|
+
|
98
|
+
def valid_string?(value)
|
99
|
+
!(value.is_a?(Array) || value.is_a?(Hash))
|
100
|
+
end
|
99
101
|
|
100
102
|
def valid_name?(value)
|
101
|
-
|
103
|
+
valid_string?(value) && !value.to_s.empty?
|
102
104
|
end
|
103
|
-
private_class_method :valid_name?
|
104
105
|
end
|
105
106
|
end
|
data/lib/heytmux/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: heytmux
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Junegunn Choi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-07-
|
11
|
+
date: 2017-07-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -110,6 +110,11 @@ files:
|
|
110
110
|
- heytmux.gemspec
|
111
111
|
- lib/heytmux.rb
|
112
112
|
- lib/heytmux/core.rb
|
113
|
+
- lib/heytmux/pane_action.rb
|
114
|
+
- lib/heytmux/pane_actions/expect.rb
|
115
|
+
- lib/heytmux/pane_actions/keys.rb
|
116
|
+
- lib/heytmux/pane_actions/paste.rb
|
117
|
+
- lib/heytmux/pane_actions/sleep.rb
|
113
118
|
- lib/heytmux/tmux.rb
|
114
119
|
- lib/heytmux/validations.rb
|
115
120
|
- lib/heytmux/version.rb
|
@@ -132,7 +137,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
132
137
|
version: '0'
|
133
138
|
requirements: []
|
134
139
|
rubyforge_project:
|
135
|
-
rubygems_version: 2.6.
|
140
|
+
rubygems_version: 2.6.11
|
136
141
|
signing_key:
|
137
142
|
specification_version: 4
|
138
143
|
summary: Hey tmux!
|