tmux-ruby 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.yardopts +1 -0
  2. data/LICENSE +22 -0
  3. data/README.md +23 -0
  4. data/lib/tmux.rb +56 -0
  5. data/lib/tmux/buffer.rb +131 -0
  6. data/lib/tmux/client.rb +193 -0
  7. data/lib/tmux/exception.rb +5 -0
  8. data/lib/tmux/exception/basic_exception.rb +6 -0
  9. data/lib/tmux/exception/in_tmux.rb +9 -0
  10. data/lib/tmux/exception/index_in_use.rb +9 -0
  11. data/lib/tmux/exception/unknown_command.rb +9 -0
  12. data/lib/tmux/exception/unsupported_version.rb +15 -0
  13. data/lib/tmux/filterable_hash.rb +15 -0
  14. data/lib/tmux/options.rb +109 -0
  15. data/lib/tmux/options/attr_option.rb +10 -0
  16. data/lib/tmux/options/bell_action_option.rb +19 -0
  17. data/lib/tmux/options/boolean_option.rb +26 -0
  18. data/lib/tmux/options/char_array_option.rb +26 -0
  19. data/lib/tmux/options/clock_mode_style_option.rb +27 -0
  20. data/lib/tmux/options/color_option.rb +23 -0
  21. data/lib/tmux/options/justification_option.rb +19 -0
  22. data/lib/tmux/options/keymap_option.rb +19 -0
  23. data/lib/tmux/options/number_option.rb +26 -0
  24. data/lib/tmux/options/option.rb +38 -0
  25. data/lib/tmux/options/string_option.rb +26 -0
  26. data/lib/tmux/options/symbol_option.rb +26 -0
  27. data/lib/tmux/options/word_array_option.rb +26 -0
  28. data/lib/tmux/options_list.rb +150 -0
  29. data/lib/tmux/pane.rb +496 -0
  30. data/lib/tmux/server.rb +217 -0
  31. data/lib/tmux/session.rb +312 -0
  32. data/lib/tmux/status_bar.rb +134 -0
  33. data/lib/tmux/status_bar/field.rb +129 -0
  34. data/lib/tmux/version.rb +4 -0
  35. data/lib/tmux/widget.rb +35 -0
  36. data/lib/tmux/widgets/progress_bar.rb +107 -0
  37. data/lib/tmux/window.rb +697 -0
  38. data/lib/tmux/window/status.rb +21 -0
  39. data/lib/tmux/window/status/state.rb +87 -0
  40. metadata +96 -0
@@ -0,0 +1,217 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Tmux
3
+ class Server
4
+ include Comparable
5
+
6
+ # Creates a new session
7
+ #
8
+ # @option args [Boolean] :attach (false) Attach to the new session?
9
+ # @option args [String] :name (nil) Name of the new session. Will
10
+ # be automatically generated of nil.
11
+ # @option args [String] :window_name (nil) Name of the initial
12
+ # window. Cannot be used when grouping sessions.
13
+ # @option args [Session] :group_with (nil) Group with this
14
+ # session, sharing all windows.
15
+ # @option args [String] :command (nil) Execute this command in the
16
+ # initial window. Cannot be used when grouping sessions.
17
+ #
18
+ # @return [Session, nil] Returns the new {Session session} if a
19
+ # `:name` has been given and if `:attach` is false
20
+ #
21
+ # @raise [ArgumentError] if combining `:group_with` and `:window_name`
22
+ # or :command
23
+ #
24
+ # @tmuxver >=1.4
25
+ def create_session(args = {})
26
+ check_for_version!("1.4")
27
+
28
+ if args[:group_with] && (args[:window_name] || args[:command])
29
+ raise ArgumentError, "Cannot combine :group_with and :window_name or :command"
30
+ end
31
+
32
+ # FIXME shell escape names
33
+ flags = []
34
+ flags << "-d" unless args[:attach]
35
+ flags << "-n '#{args[:window_name]}'" if args[:window_name]
36
+ flags << "-s '#{args[:name]}'" if args[:name]
37
+ flags << "-t '#{args[:group_with].name}'" if args[:group_with]
38
+ flags << args[:command] if args[:command]
39
+
40
+ command = "new-session #{flags.join(" ")}"
41
+
42
+ ret = invoke_command(command, true)
43
+ if ret.start_with?("duplicate session:")
44
+ raise RuntimeError, ret
45
+ elsif ret.start_with?("sessions should be nested with care.")
46
+ raise Exception::InTmux("new-session")
47
+ else
48
+ if args[:name] and !args[:attach]
49
+ return Session.new(self, args[:name])
50
+ end
51
+ end
52
+ end
53
+
54
+ # @return [String]
55
+ attr_reader :socket
56
+ # @return [OptionsList]
57
+ attr_reader :options
58
+ # @param [String] socket A socket *name*.
59
+ def initialize(socket = "default")
60
+ @socket = socket
61
+ @options = OptionsList.new(:server, self, false)
62
+ end
63
+
64
+ # @return [-1, 0, 1]
65
+ def <=>(other)
66
+ return nil unless other.is_a?(Server)
67
+ @socket <=> other.socket
68
+ end
69
+
70
+ # @return [Server] Returns self. This is useful for other classes
71
+ # which can operate on Server, {Session}, {Window}, {Pane} and so
72
+ # on
73
+ attr_reader :server
74
+ undef_method "server"
75
+ def server
76
+ self
77
+ end
78
+
79
+ # Invokes a tmux command.
80
+ #
81
+ # @param [String] command The command to invoke
82
+ # @return [void]
83
+ def invoke_command(command, unset_tmux = false)
84
+ Tmux.invoke_command("-L #@socket #{command}", unset_tmux)
85
+ end
86
+
87
+ # Kills a server and thus all {Session sessions}, {Window windows} and {Client clients}.
88
+ #
89
+ # @tmux kill-server
90
+ # @return [void]
91
+ def kill
92
+ invoke_command "kill-server"
93
+ end
94
+
95
+ # Sources a file, that is load and evaluate it in tmux.
96
+ #
97
+ # @param [String] file Name of the file to source
98
+ # @tmux source-file
99
+ # @return [void]
100
+ def source_file(file)
101
+ invoke_command "source-file #{file}"
102
+ end
103
+ alias_method :load, :source_file
104
+
105
+ # @tmux list-sessions
106
+ # @param [Hash] search Filters the resulting hash using {FilterableHash#filter}
107
+ # @return [Hash] A hash with information for all sessions
108
+ def sessions_information(search = {})
109
+ hash = {}
110
+ output = invoke_command "list-sessions"
111
+ output.each_line do |session|
112
+ params = session.match(/^(?<name>\w+?): (?<num_windows>\d+) windows \(created (?<creation_time>.+?)\) \[(?<width>\d+)x(?<height>\d+)\](?: \((?<attached>attached)\))?$/)
113
+
114
+ name = params[:name]
115
+ num_windows = params[:num_windows].to_i
116
+ creation_time = Date.parse(params[:creation_time])
117
+ width = params[:width].to_i
118
+ height = params[:height].to_i
119
+ attached = !!params[:attached]
120
+
121
+ hash[name] = {
122
+ :name => name,
123
+ :num_windows => num_windows,
124
+ :creation_time => creation_time,
125
+ :width => width,
126
+ :height => height,
127
+ :attached => attached,
128
+ }
129
+ end
130
+ hash.extend FilterableHash
131
+ hash.filter(search)
132
+ end
133
+
134
+ # @tmux list-sessions
135
+ # @return [Array<Session>] All {Session sessions}
136
+ attr_reader :sessions
137
+ undef_method "sessions"
138
+ def sessions(search = {})
139
+ sessions_information(search).map do |name, information|
140
+ Session.new(self, name)
141
+ end
142
+ end
143
+
144
+ # @return [Session] The first {Session session}. This is
145
+ # especially useful if working with a server that only has one
146
+ # {Session session}.
147
+ attr_reader :session
148
+ undef_method "session"
149
+ def session
150
+ sessions.first
151
+ end
152
+
153
+ # @tmux list-clients
154
+ # @param [Hash] search Filters the resulting hash using {FilterableHash#filter}
155
+ # @return [Hash] A hash with information for all clients
156
+ def clients_information(search = {})
157
+ clients = invoke_command "list-clients"
158
+ hash = {}
159
+ clients.each_line do |client|
160
+ params = client.match(/^(?<device>.+?): (?<session>\d+) \[(?<width>\d+)x(?<height>\d+) (?<term>.+?)\](?: \((?<utf8>utf8)\))?$/)
161
+ device = params[:device]
162
+ session = sessions[params[:session].to_i]
163
+ width = params[:width].to_i
164
+ height = params[:height].to_i
165
+ term = params[:term]
166
+ utf8 = !!params[:utf8]
167
+
168
+ hash[device] = {
169
+ :device => device,
170
+ :session => session,
171
+ :width => width,
172
+ :height => height,
173
+ :term => term,
174
+ :utf8 => utf8,
175
+ }
176
+ end
177
+ hash.extend FilterableHash
178
+ hash.filter(search)
179
+ end
180
+
181
+ # @tmux list-clients
182
+ # @return [Array<Client>]
183
+ attr_reader :clients
184
+ undef_method "clients"
185
+ def clients(search = {})
186
+ clients_information(search).map { |device, information|
187
+ Client.new(self, device)
188
+ }
189
+ end
190
+
191
+ # @tmux server-info
192
+ # @return [String] Information about the server
193
+ attr_reader :info
194
+ undef_method "info"
195
+ def info
196
+ invoke_command "server-info"
197
+ end
198
+
199
+ # @return [String] Version of the tmux server
200
+ attr_reader :version
201
+ undef_method "version"
202
+ def version
203
+ @version ||= info.lines.first.split(",").first[/([.\d]+)/]
204
+ end
205
+
206
+ # Checks if a version requirement is being met
207
+ #
208
+ # @param [String] required The version at least required
209
+ # @raise [Exception::UnsupportedVersion] Raised if a version requirement isn't met
210
+ # @return [void]
211
+ def check_for_version!(required)
212
+ if required > version
213
+ raise Exception::UnsupportedVersion, required
214
+ end
215
+ end
216
+ end
217
+ end
@@ -0,0 +1,312 @@
1
+ module Tmux
2
+ # A session is a single collection of pseudo terminals under the
3
+ # management of {Tmux tmux}. Each session has one or more {Window
4
+ # windows} linked to it. A {Window window} occupies the entire
5
+ # screen and may be split into rectangular {Pane panes}, each of
6
+ # which is a separate pseudo terminal (the pty(4) manual page
7
+ # documents the technical details of pseudo terminals). Any number
8
+ # of tmux instances may connect to the same session, and any number
9
+ # of {Window windows} may be present in the same session. Once all
10
+ # sessions are {Session#kill killed}, tmux exits.
11
+ class Session
12
+ include Comparable
13
+
14
+ # @return [Options]
15
+ def self.options(session)
16
+ OptionsList.new(:session, session, true)
17
+ end
18
+
19
+ # Creates a new {Window window}.
20
+ #
21
+ # @option args [Boolean] :after_number (false) If true, the new
22
+ # {Window window} will be inserted at the next index up from the
23
+ # specified number (or the {Client#current_window current}
24
+ # {Window window}), moving {Window windows} up if necessary
25
+ # @option args [Boolean] :kill_existing (false) Kill an existing
26
+ # {Window window} if it conflicts with a desired number
27
+ # @option args [Boolean] :make_active (true) Switch to the newly
28
+ # generated {Window window}
29
+ # @option args [String] :name Name of the new {Window window}
30
+ # (optional)
31
+ # @option args [Number] :number Number of the new {Window window}
32
+ # (optional)
33
+ # @option args [String] :command Command to run in the new {Window
34
+ # window} (optional)
35
+ #
36
+ # @tmux new-window
37
+ # @return [Window] The newly created {Window window}
38
+ def create_window(args = {})
39
+ args = {
40
+ :kill_existing => false,
41
+ :make_active => true,
42
+ :after_number => false,
43
+ }.merge(args)
44
+
45
+ flags = []
46
+ # flags << "-d" unless args[:make_active]
47
+ flags << "-a" if args[:after_number]
48
+ flags << "-k" if args[:kill_existing]
49
+ flags << "-n '#{args[:name]}'" if args[:name] # FIXME escaping
50
+ flags << "-t #{args[:number]}" if args[:number]
51
+ flags << args[:command] if args[:command]
52
+
53
+ @server.invoke_command "new-window #{flags.join(" ")}"
54
+ new_window = current_window
55
+ unless args[:make_active]
56
+ select_last_window
57
+ end
58
+ # return Window.new(self, num)
59
+ return new_window
60
+ end
61
+
62
+ # @see Client#current_window
63
+ # @return (see Client#current_window)
64
+ attr_reader :current_window
65
+ undef_method "current_window"
66
+ def current_window
67
+ any_client.current_window
68
+ end
69
+
70
+ # @see Client#current_pane
71
+ # @return (see Client#current_pane)
72
+ attr_reader :current_pane
73
+ undef_method "current_pane"
74
+ def current_pane
75
+ any_client.current_pane
76
+ end
77
+
78
+ # Returns a {Client client} that is displaying the session.
79
+ #
80
+ # @return [Client, nil] A {Client client} that is displaying the session.
81
+ def any_client
82
+ @server.clients({:session => self}).first
83
+ end
84
+
85
+ # @return [Boolean]
86
+ def ==(other)
87
+ self.class == other.class && @server == other.server && @name == other.name
88
+ end
89
+
90
+ # @return [Number]
91
+ def hash
92
+ [@server.hash, @number].hash
93
+ end
94
+
95
+ # @return [Boolean]
96
+ def eql?(other)
97
+ self == other
98
+ end
99
+
100
+ def <=>(other)
101
+ return nil unless other.is_a?(Session)
102
+ [@server, @name] <=> [other.server, other.name]
103
+ end
104
+
105
+ # @overload name
106
+ # @return [String]
107
+ # @overload name=(new_name)
108
+ # Renames the session.
109
+ #
110
+ # @todo escape name
111
+ # @return [String]
112
+ # @tmux rename-session
113
+ # @return [String]
114
+ attr_accessor :name
115
+ undef_method "name="
116
+ # @return [Server]
117
+ attr_reader :server
118
+ # @return [OptionsList]
119
+ attr_reader :options
120
+ # @return [StatusBar]
121
+ attr_reader :status_bar
122
+ def initialize(server, name)
123
+ @server, @name = server, name
124
+ @status_bar = StatusBar.new(self)
125
+ @options = OptionsList.new(:session, self, false)
126
+ end
127
+
128
+ def name=(new_name)
129
+ raise ArgumentError if new_name.to_s.strip.empty?
130
+ ret = @server.invoke_command("rename-session -t #{identifier} '#{new_name}'")
131
+
132
+ if ret.start_with?("duplicate session:")
133
+ raise RuntimeError, ret
134
+ end
135
+
136
+ @name = new_name
137
+ end
138
+
139
+ # @return [String]
140
+ attr_reader :identifier
141
+ undef_method "identifier"
142
+ def identifier
143
+ @name
144
+ end
145
+
146
+ # Locks the session.
147
+ #
148
+ # @tmux lock-session
149
+ # @return [void]
150
+ # @tmuxver &gt;=1.1
151
+ def lock
152
+ @server.check_for_version!("1.1")
153
+
154
+ @server.invoke_command "lock-session -t #{identifier}"
155
+ end
156
+
157
+ # @return [Integer]
158
+ attr_reader :num_windows
159
+ undef_method "num_windows"
160
+ def num_windows
161
+ @server.sessions_information[@name][:num_windows]
162
+ end
163
+
164
+ # @return [Time]
165
+ attr_reader :creation_time
166
+ undef_method "creation_time"
167
+ def creation_time
168
+ @server.sessions_information[@name][:creation_time]
169
+ end
170
+ alias_method :created_at, :creation_time
171
+
172
+ # @return [Integer]
173
+ attr_reader :width
174
+ undef_method "width"
175
+ def width
176
+ @server.sessions_information[@name][:width]
177
+ end
178
+
179
+ # @return [Integer]
180
+ attr_reader :height
181
+ undef_method "height"
182
+ def height
183
+ @server.sessions_information[@name][:height]
184
+ end
185
+
186
+ # @return [Boolean]
187
+ attr_reader :attached
188
+ undef_method "attached"
189
+ def attached
190
+ @server.sessions_information[@name][:attached]
191
+ end
192
+ alias_method :attached?, :attached
193
+
194
+ # @return [Array<Client>] All {Client clients}
195
+ attr_reader :clients
196
+ undef_method "clients"
197
+ def clients
198
+ @server.clients({:session => self})
199
+ end
200
+
201
+ # Attach to a session. Replaces the ruby process.
202
+ #
203
+ # @return [void]
204
+ # @tmux attach
205
+ def attach
206
+ exec "#{Tmux::BINARY} attach -t #{identifier}"
207
+ end
208
+
209
+ # Kills the session.
210
+ #
211
+ # @tmux kill-session
212
+ # @return [void]
213
+ def kill
214
+ @server.invoke_command "kill-session -t #{identifier}"
215
+ end
216
+
217
+ # @tmux list-windows
218
+ # @tmuxver &gt;=1.1
219
+ # @param [Hash] search Filters the resulting hash using {FilterableHash#filter}
220
+ # @return [Hash] A hash with information for all windows
221
+ # @return [Hash]
222
+ def windows_information(search = {})
223
+ @server.check_for_version!("1.1")
224
+
225
+ hash = {}
226
+ output = @server.invoke_command "list-windows -t #{identifier}"
227
+ output.each_line do |session|
228
+ params = session.match(/^(?<num>\d+): (?<name>.+?) \[(?<width>\d+)x(?<height>\d+)\]$/)
229
+ next if params.nil? # >=1.3 displays layout information in indented lines
230
+ num = params[:num].to_i
231
+ name = params[:name]
232
+ width = params[:width].to_i
233
+ height = params[:height].to_i
234
+
235
+ hash[num] = {:num => num, :name => name, :width => width, :height => height}
236
+ end
237
+ hash.extend FilterableHash
238
+ hash.filter(search)
239
+ end
240
+
241
+ # @tmux list-windows
242
+ # @return [Hash{Number => Window}] All {Window windows}
243
+ # @tmuxver &gt;=1.1
244
+ attr_reader :windows
245
+ undef_method "windows"
246
+ def windows
247
+ hash = {}
248
+ @server.check_for_version!("1.1")
249
+
250
+ windows_information.each do |num, information|
251
+ hash[num] = Window.new(self, num)
252
+ end
253
+ hash
254
+ end
255
+
256
+ # @param [Hash] search Filters the resulting hash using {FilterableHash#filter}
257
+ # @return [Hash] A hash with information for all buffers
258
+ # @tmux list-buffers
259
+ def buffers_information(search = {})
260
+ hash = {}
261
+ buffers = @server.invoke_command "list-buffers -t #{identifier}"
262
+ buffers.each_line do |buffer|
263
+ num, size = buffer.match(/^(\d+): (\d+) bytes/)[1..2]
264
+ hash[num] = {:size => size}
265
+ end
266
+ hash.extend FilterableHash
267
+ hash.filter(search)
268
+ end
269
+
270
+ # @tmux list-buffers
271
+ # @return [Array<Buffer>] All {Buffer buffers}
272
+ attr_reader :buffers
273
+ undef_method "buffers"
274
+ def buffers
275
+ buffers_information.map do |num, information|
276
+ Buffer.new(num, self)
277
+ end
278
+ end
279
+
280
+ # @group Selecting
281
+
282
+ # Select the last (previously selected) window.
283
+ #
284
+ # @return [Window]
285
+ def select_last_window
286
+ @server.invoke_command "last-window -t #{identifier}"
287
+ current_window
288
+ end
289
+
290
+ # Selects the next (higher index) window
291
+ #
292
+ # @param [Number] num How many windows to move
293
+ # @tmuxver &gt;=1.3
294
+ # @return [Window]
295
+ def select_next_window(num = 1)
296
+ @server.invoke_command "select-window -t #{identifier}:+#{num}"
297
+ current_window
298
+ end
299
+
300
+ # Selects the previous (lower index) window
301
+ #
302
+ # @param [Number] num How many windows to move
303
+ # @tmuxver &gt;=1.3
304
+ # @return [Window]
305
+ def select_previous_window(num = 1)
306
+ @server.invoke_command "select-window -t:-#{num}"
307
+ current_window
308
+ end
309
+
310
+ # @endgroup
311
+ end
312
+ end