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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cb566cb530c83ebd00f2dcfb707eb0e992593ee7
4
- data.tar.gz: a0a6b6a585a503d1d5ceab93752345b897c125b2
3
+ metadata.gz: 53af5daf1e2193c29b0a85affdb4fc72b739b536
4
+ data.tar.gz: 76fe05372f8e0252556ae26cbcb98f40579dca4c
5
5
  SHA512:
6
- metadata.gz: 3e768bf1f0f205cfe32546253338856ca6d0a6d6742cf8f6ab9244e146be6cf2978823114517cad0554a10ea94e7fbd65e8f54fdeda84a85ed04135a23b4632d
7
- data.tar.gz: 2df1f6fcff06149be16865c6991238519f5163bebaca3180c2a410a7dbf764edcadfd404c56b84642599d5cd335848d739037f3f7bc5f544d5bbc41a1decafc4
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 the pane (a la [Expect][expect]).
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
 
@@ -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'
@@ -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
- case command
76
- when Hash
77
- _action, regex = command.first
78
- regex = Regexp.compile(regex.to_s)
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
@@ -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
- case commands
71
- when Array
72
- hashes, strings = commands.partition { |command| command.is_a?(Hash) }
73
- hashes.each do |command|
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
- action, condition = command.first
76
- unless SUPPORTED_ACTIONS.include?(action)
77
- raise ArgumentError, "Unsupported action: #{action}"
78
- end
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
- private_class_method :single_spec?
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
- !(value.nil? || value.is_a?(Array) || value.to_s.empty?)
103
+ valid_string?(value) && !value.to_s.empty?
102
104
  end
103
- private_class_method :valid_name?
104
105
  end
105
106
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Heytmux
4
- VERSION = '0.0.0'
4
+ VERSION = '0.1.0'
5
5
  end
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.0.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-03 00:00:00.000000000 Z
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.8
140
+ rubygems_version: 2.6.11
136
141
  signing_key:
137
142
  specification_version: 4
138
143
  summary: Hey tmux!