tty-screen 0.5.1 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +16 -0
- data/README.md +12 -8
- data/lib/tty/screen.rb +241 -35
- data/lib/tty/version.rb +5 -0
- data/lib/tty-screen.rb +1 -1
- data/spec/unit/height_width_spec.rb +31 -0
- data/spec/unit/size_spec.rb +124 -53
- data/tasks/console.rake +1 -0
- data/tty-screen.gemspec +3 -1
- metadata +6 -7
- data/lib/tty/screen/size.rb +0 -155
- data/lib/tty/screen/version.rb +0 -5
- data/spec/unit/new_spec.rb +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dae2d40de8e2675bc91d26ec698d333bce317169
|
4
|
+
data.tar.gz: 2b26bc3794b5a6b05ff230b9202a11b59c60a8f3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d16dba65921f31a54f1c5528e89597ab223c6aecc1dccec83d3c588ae6e7b341b643f98746be1236e5135513752a07fad79c2eccda26218fce8585742430d5d4
|
7
|
+
data.tar.gz: 4083a1c8b373ff2dbb64fb2856f8ab5c8c9bcab29974194666ab5db02cd206876027e44a80473625bdad4827fe6571134fe1cee15ad1953c7fe7ab75f076f40e
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,20 @@
|
|
1
1
|
# Change log
|
2
2
|
|
3
|
+
## [v0.6.0] - 2017-10-29
|
4
|
+
|
5
|
+
### Added
|
6
|
+
* Add #size_from_ioctl check for reading terminal size with Unix ioctl
|
7
|
+
* Add #size_from_java check for reading terminal size from Java on JRuby
|
8
|
+
* Add #size_from_win_api check for reading terminal size from Windows C API
|
9
|
+
|
10
|
+
### Changed
|
11
|
+
* Change TTY::Screen to a module without any state
|
12
|
+
* Change to prefix all checks with `size` keyword
|
13
|
+
* Change gemspec to require ruby >= 2.0.0
|
14
|
+
* Remove #try_io_console and inline with io-console check
|
15
|
+
* Remove #default_size and replace with constant
|
16
|
+
* Remove TTY::Screen::Size class
|
17
|
+
|
3
18
|
## [v0.5.1] - 2017-10-26
|
4
19
|
|
5
20
|
### Changed
|
@@ -56,6 +71,7 @@
|
|
56
71
|
### Fixed
|
57
72
|
* Fix bug with screen detection from_io_console by @luxflux
|
58
73
|
|
74
|
+
[v0.6.0]: https://github.com/peter-murach/tty-screen/compare/v0.5.1...v0.6.0
|
59
75
|
[v0.5.1]: https://github.com/peter-murach/tty-screen/compare/v0.5.0...v0.5.1
|
60
76
|
[v0.5.0]: https://github.com/peter-murach/tty-screen/compare/v0.4.3...v0.5.0
|
61
77
|
[v0.4.3]: https://github.com/peter-murach/tty-screen/compare/v0.4.2...v0.4.3
|
data/README.md
CHANGED
@@ -17,7 +17,7 @@
|
|
17
17
|
|
18
18
|
> Terminal screen size detection which works on Linux, OS X and Windows/Cygwin platforms and supports MRI, JRuby and Rubinius interpreters.
|
19
19
|
|
20
|
-
**TTY::Screen** provides independent screen size detection component for [TTY](https://github.com/piotrmurach/tty) toolkit.
|
20
|
+
**TTY::Screen** provides independent terminal screen size detection component for [TTY](https://github.com/piotrmurach/tty) toolkit.
|
21
21
|
|
22
22
|
## Installation
|
23
23
|
|
@@ -40,19 +40,23 @@ Or install it yourself as:
|
|
40
40
|
**TTY::Screen** allows you to detect terminal screen size by calling `size` method which returns [height, width] tuple.
|
41
41
|
|
42
42
|
```ruby
|
43
|
-
|
44
|
-
|
45
|
-
screen.size # => [51, 280]
|
46
|
-
screen.width # => 280
|
47
|
-
screen.height # => 51
|
43
|
+
TTY::Screen.size # => [51, 280]
|
48
44
|
```
|
49
45
|
|
50
|
-
|
46
|
+
To read terminal width do:
|
51
47
|
|
52
48
|
```ruby
|
53
|
-
TTY::Screen.size # => [51, 280]
|
54
49
|
TTY::Screen.width # => 280
|
50
|
+
TTY::Screen.columns # => 280
|
51
|
+
TTY::Screen.cols # => 280
|
52
|
+
```
|
53
|
+
|
54
|
+
Similarly, to read terminal height do:
|
55
|
+
|
56
|
+
```ruby
|
55
57
|
TTY::Screen.height # => 51
|
58
|
+
TTY::Screen.rows # => 51
|
59
|
+
TTY::Screen.lines # => 51
|
56
60
|
```
|
57
61
|
|
58
62
|
## Contributing
|
data/lib/tty/screen.rb
CHANGED
@@ -1,66 +1,272 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# fronzen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'version'
|
3
4
|
|
4
5
|
module TTY
|
5
6
|
# Used for detecting screen properties
|
6
7
|
#
|
7
8
|
# @api public
|
8
|
-
|
9
|
-
#
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
module Screen
|
10
|
+
# Helper to define private functions
|
11
|
+
def self.private_module_function(name)
|
12
|
+
module_function(name)
|
13
|
+
private_class_method(name)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Default terminal size
|
14
17
|
#
|
15
18
|
# @api public
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
19
|
+
DEFAULT_SIZE = [27, 80].freeze
|
20
|
+
|
21
|
+
@env = ENV
|
22
|
+
@output = $stderr
|
23
|
+
|
24
|
+
class << self
|
25
|
+
attr_accessor :env
|
26
|
+
|
27
|
+
# Specifies an output stream
|
28
|
+
#
|
29
|
+
# @api public
|
30
|
+
attr_accessor :output
|
20
31
|
end
|
21
32
|
|
33
|
+
# Get terminal rows and columns
|
34
|
+
#
|
35
|
+
# @return [Array[Integer, Integer]]
|
36
|
+
# return rows & columns
|
37
|
+
#
|
22
38
|
# @api public
|
23
|
-
def
|
24
|
-
|
39
|
+
def size
|
40
|
+
size = size_from_java
|
41
|
+
size ||= size_from_win_api
|
42
|
+
size ||= size_from_ioctl
|
43
|
+
size ||= size_from_io_console
|
44
|
+
size ||= size_from_readline
|
45
|
+
size ||= size_from_tput
|
46
|
+
size ||= size_from_stty
|
47
|
+
size ||= size_from_env
|
48
|
+
size ||= size_from_ansicon
|
49
|
+
size || DEFAULT_SIZE
|
25
50
|
end
|
51
|
+
module_function :size
|
26
52
|
|
27
|
-
|
28
|
-
def self.width
|
53
|
+
def width
|
29
54
|
size[1]
|
30
55
|
end
|
56
|
+
module_function :width
|
31
57
|
|
32
|
-
|
33
|
-
|
58
|
+
alias columns width
|
59
|
+
alias cols width
|
60
|
+
module_function :columns
|
61
|
+
module_function :cols
|
62
|
+
|
63
|
+
def height
|
34
64
|
size[0]
|
35
65
|
end
|
66
|
+
module_function :height
|
67
|
+
|
68
|
+
alias rows height
|
69
|
+
alias lines height
|
70
|
+
module_function :rows
|
71
|
+
module_function :lines
|
36
72
|
|
37
|
-
|
73
|
+
STDOUT_HANDLE = 0xFFFFFFF5
|
74
|
+
|
75
|
+
# Determine terminal size with a Windows native API
|
38
76
|
#
|
39
|
-
# @return [Array[Integer]]
|
77
|
+
# @return [nil, Array[Integer, Integer]]
|
40
78
|
#
|
41
|
-
# @api
|
42
|
-
def
|
43
|
-
|
79
|
+
# @api private
|
80
|
+
def size_from_win_api(verbose: nil)
|
81
|
+
require 'fiddle'
|
82
|
+
|
83
|
+
kernel32 = Fiddle::Handle.new('kernel32')
|
84
|
+
get_std_handle = Fiddle::Function.new(kernel32['GetStdHandle'],
|
85
|
+
[-Fiddle::TYPE_INT], Fiddle::TYPE_INT)
|
86
|
+
get_console_buffer_info = Fiddle::Function.new(
|
87
|
+
kernel32['GetConsoleScreenBufferInfo'],
|
88
|
+
[Fiddle::TYPE_LONG, Fiddle::TYPE_VOIDP], Fiddle::TYPE_INT)
|
89
|
+
|
90
|
+
format = 'SSSSSssssSS'
|
91
|
+
buffer = ([0] * format.size).pack(format)
|
92
|
+
stdout_handle = get_std_handle.(STDOUT_HANDLE)
|
93
|
+
|
94
|
+
get_console_buffer_info.(stdout_handle, buffer)
|
95
|
+
_, _, _, _, _, left, top, right, bottom, = buffer.unpack(format)
|
96
|
+
[bottom - top + 1, right - left + 1]
|
97
|
+
rescue LoadError
|
98
|
+
warn 'no native fiddle module found' if verbose
|
99
|
+
rescue Fiddle::DLError
|
100
|
+
# non windows platform or no kernel32 lib
|
44
101
|
end
|
102
|
+
module_function :size_from_win_api
|
45
103
|
|
46
|
-
#
|
104
|
+
# Determine terminal size on jruby using native Java libs
|
47
105
|
#
|
48
|
-
# @return [Integer]
|
106
|
+
# @return [nil, Array[Integer, Integer]]
|
49
107
|
#
|
50
|
-
# @api
|
51
|
-
def
|
52
|
-
|
108
|
+
# @api private
|
109
|
+
def size_from_java(verbose: nil)
|
110
|
+
return unless jruby?
|
111
|
+
require 'java'
|
112
|
+
java_import 'jline.TerminalFactory'
|
113
|
+
terminal = TerminalFactory.get
|
114
|
+
[terminal.get_height, terminal.get_width]
|
115
|
+
rescue
|
116
|
+
warn 'failed to import java terminal package' if verbose
|
53
117
|
end
|
54
|
-
|
118
|
+
module_function :size_from_java
|
55
119
|
|
56
|
-
#
|
120
|
+
# Detect screen size by loading io/console lib
|
57
121
|
#
|
58
|
-
#
|
122
|
+
# On Windows io_console falls back to reading environment
|
123
|
+
# variables. This means any user changes to the terminal
|
124
|
+
# size won't be reflected in the runtime of the Ruby app.
|
59
125
|
#
|
60
|
-
# @
|
61
|
-
|
62
|
-
|
126
|
+
# @return [nil, Array[Integer, Integer]]
|
127
|
+
#
|
128
|
+
# @api private
|
129
|
+
def size_from_io_console(verbose: nil)
|
130
|
+
return if jruby?
|
131
|
+
require 'io/console'
|
132
|
+
|
133
|
+
begin
|
134
|
+
if @output.tty? && IO.method_defined?(:winsize)
|
135
|
+
size = @output.winsize
|
136
|
+
size if nonzero_column?(size[1])
|
137
|
+
end
|
138
|
+
rescue Errno::EOPNOTSUPP
|
139
|
+
# no support for winsize on output
|
140
|
+
end
|
141
|
+
rescue LoadError
|
142
|
+
warn 'no native io/console support or io-console gem' if verbose
|
143
|
+
end
|
144
|
+
module_function :size_from_io_console
|
145
|
+
|
146
|
+
TIOCGWINSZ = 0x5413
|
147
|
+
TIOCGWINSZ_PPC = 0x40087468
|
148
|
+
|
149
|
+
# Read terminal size from Unix ioctl
|
150
|
+
#
|
151
|
+
# @return [nil, Array[Integer, Integer]]
|
152
|
+
#
|
153
|
+
# @api private
|
154
|
+
def size_from_ioctl
|
155
|
+
return if jruby?
|
156
|
+
return unless @output.respond_to?(:ioctl)
|
157
|
+
|
158
|
+
format = 'SSSS'
|
159
|
+
buffer = ([0] * format.size).pack(format)
|
160
|
+
if ioctl?(TIOCGWINSZ, buffer) || ioctl?(TIOCGWINSZ_PPC, buffer)
|
161
|
+
rows, cols, = buffer.unpack(format)[0..1]
|
162
|
+
return [rows, cols]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
module_function :size_from_ioctl
|
166
|
+
|
167
|
+
def ioctl?(control, buf)
|
168
|
+
@output.ioctl(control, buf) >= 0
|
169
|
+
rescue Errno::ENOTTY
|
170
|
+
# wrong processor architecture
|
171
|
+
false
|
172
|
+
rescue Errno::EINVAL
|
173
|
+
# ioctl failed to recognise processor type(not Intel)
|
174
|
+
false
|
175
|
+
end
|
176
|
+
module_function :ioctl?
|
177
|
+
|
178
|
+
# Detect screen size using Readline
|
179
|
+
#
|
180
|
+
# @api private
|
181
|
+
def size_from_readline
|
182
|
+
if defined?(Readline) && Readline.respond_to?(:get_screen_size)
|
183
|
+
size = Readline.get_screen_size
|
184
|
+
size if nonzero_column?(size[1])
|
185
|
+
end
|
186
|
+
rescue NotImplementedError
|
187
|
+
end
|
188
|
+
module_function :size_from_readline
|
189
|
+
|
190
|
+
# Detect terminal size from tput utility
|
191
|
+
#
|
192
|
+
# @api private
|
193
|
+
def size_from_tput
|
194
|
+
return unless @output.tty?
|
195
|
+
lines = run_command('tput', 'lines').to_i
|
196
|
+
cols = run_command('tput', 'cols').to_i
|
197
|
+
[lines, cols] if nonzero_column?(lines)
|
198
|
+
rescue Errno::ENOENT
|
199
|
+
end
|
200
|
+
module_function :size_from_tput
|
201
|
+
|
202
|
+
# Detect terminal size from stty utility
|
203
|
+
#
|
204
|
+
# @api private
|
205
|
+
def size_from_stty
|
206
|
+
return unless @output.tty?
|
207
|
+
out = run_command('stty', 'size')
|
208
|
+
return unless out
|
209
|
+
size = out.split.map(&:to_i)
|
210
|
+
size if nonzero_column?(size[1])
|
211
|
+
rescue Errno::ENOENT
|
212
|
+
end
|
213
|
+
module_function :size_from_stty
|
214
|
+
|
215
|
+
# Detect terminal size from environment
|
216
|
+
#
|
217
|
+
# After executing Ruby code if the user changes terminal
|
218
|
+
# dimensions during code runtime, the code won't be notified,
|
219
|
+
# and hence won't see the new dimensions reflected in its copy
|
220
|
+
# of LINES and COLUMNS environment variables.
|
221
|
+
#
|
222
|
+
# @return [nil, Array[Integer, Integer]]
|
223
|
+
#
|
224
|
+
# @api private
|
225
|
+
def size_from_env
|
226
|
+
return unless @env['COLUMNS'] =~ /^\d+$/
|
227
|
+
size = [(@env['LINES'] || @env['ROWS']).to_i, @env['COLUMNS'].to_i]
|
228
|
+
size if nonzero_column?(size[1])
|
229
|
+
end
|
230
|
+
module_function :size_from_env
|
231
|
+
|
232
|
+
# Detect terminal size from Windows ANSICON
|
233
|
+
#
|
234
|
+
# @api private
|
235
|
+
def size_from_ansicon
|
236
|
+
return unless @env['ANSICON'] =~ /\((.*)x(.*)\)/
|
237
|
+
size = [$2, $1].map(&:to_i)
|
238
|
+
size if nonzero_column?(size[1])
|
239
|
+
end
|
240
|
+
module_function :size_from_ansicon
|
241
|
+
|
242
|
+
# Runs command silently capturing the output
|
243
|
+
#
|
244
|
+
# @api private
|
245
|
+
def run_command(*args)
|
246
|
+
require 'tempfile'
|
247
|
+
out = Tempfile.new('tty-screen')
|
248
|
+
result = system(*args, out: out.path)
|
249
|
+
return if result.nil?
|
250
|
+
out.rewind
|
251
|
+
out.read
|
252
|
+
ensure
|
253
|
+
out.close if out
|
254
|
+
end
|
255
|
+
private_module_function :run_command
|
256
|
+
|
257
|
+
# Check if number is non zero
|
258
|
+
#
|
259
|
+
# return [Boolean]
|
260
|
+
#
|
261
|
+
# @api private
|
262
|
+
def nonzero_column?(column)
|
263
|
+
column.to_i > 0
|
264
|
+
end
|
265
|
+
private_module_function :nonzero_column?
|
266
|
+
|
267
|
+
def jruby?
|
268
|
+
RbConfig::CONFIG['ruby_install_name'] == 'jruby'
|
63
269
|
end
|
64
|
-
|
270
|
+
private_module_function :jruby?
|
65
271
|
end # Screen
|
66
272
|
end # TTY
|
data/lib/tty/version.rb
ADDED
data/lib/tty-screen.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
|
1
|
+
require_relative 'tty/screen'
|
@@ -0,0 +1,31 @@
|
|
1
|
+
RSpec.describe TTY::Screen, '#height,#width' do
|
2
|
+
it "calcualtes screen width" do
|
3
|
+
allow(TTY::Screen).to receive(:size).and_return([51, 280])
|
4
|
+
expect(TTY::Screen.width).to eq(280)
|
5
|
+
end
|
6
|
+
|
7
|
+
it "aliases width to columns" do
|
8
|
+
allow(TTY::Screen).to receive(:size).and_return([51, 280])
|
9
|
+
expect(TTY::Screen.columns).to eq(280)
|
10
|
+
end
|
11
|
+
|
12
|
+
it "aliases width to cols" do
|
13
|
+
allow(TTY::Screen).to receive(:size).and_return([51, 280])
|
14
|
+
expect(TTY::Screen.cols).to eq(280)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "calcualtes screen height" do
|
18
|
+
allow(TTY::Screen).to receive(:size).and_return([51, 280])
|
19
|
+
expect(TTY::Screen.height).to eq(51)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "aliases width to rows" do
|
23
|
+
allow(TTY::Screen).to receive(:size).and_return([51, 280])
|
24
|
+
expect(TTY::Screen.rows).to eq(51)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "aliases width to lines" do
|
28
|
+
allow(TTY::Screen).to receive(:size).and_return([51, 280])
|
29
|
+
expect(TTY::Screen.lines).to eq(51)
|
30
|
+
end
|
31
|
+
end
|
data/spec/unit/size_spec.rb
CHANGED
@@ -1,34 +1,77 @@
|
|
1
1
|
require 'delegate'
|
2
2
|
|
3
|
-
RSpec.describe TTY::Screen
|
3
|
+
RSpec.describe TTY::Screen, '#size' do
|
4
4
|
class Output < SimpleDelegator
|
5
5
|
def winsize
|
6
6
|
[100, 200]
|
7
7
|
end
|
8
|
+
|
9
|
+
def ioctl(control, buf)
|
10
|
+
buf.replace("3\x00\xD3\x00\xF2\x04\xCA\x02\x00")
|
11
|
+
0
|
12
|
+
end
|
8
13
|
end
|
9
14
|
|
10
15
|
let(:output) { Output.new(StringIO.new('', 'w+')) }
|
11
16
|
|
12
17
|
context 'size' do
|
13
18
|
it "correctly falls through choices" do
|
14
|
-
screen =
|
15
|
-
|
16
|
-
|
19
|
+
screen = TTY::Screen
|
20
|
+
old_output = screen.output
|
21
|
+
screen.output = output
|
22
|
+
|
23
|
+
allow(screen).to receive(:size_from_java).and_return(nil)
|
24
|
+
allow(screen).to receive(:size_from_win_api).and_return(nil)
|
25
|
+
allow(screen).to receive(:size_from_ioctl).and_return(nil)
|
26
|
+
allow(screen).to receive(:size_from_io_console).and_return([51, 280])
|
27
|
+
allow(screen).to receive(:size_from_readline).and_return(nil)
|
17
28
|
|
18
29
|
expect(screen.size).to eq([51, 280])
|
19
|
-
expect(screen).
|
30
|
+
expect(screen).to have_received(:size_from_java)
|
31
|
+
expect(screen).to have_received(:size_from_win_api)
|
32
|
+
expect(screen).to have_received(:size_from_ioctl)
|
33
|
+
expect(screen).to_not have_received(:size_from_readline)
|
34
|
+
|
35
|
+
screen.output = old_output
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "size from java" do
|
40
|
+
it "doesn't import java on non-jruby platform" do
|
41
|
+
screen = TTY::Screen
|
42
|
+
allow(screen).to receive(:jruby?).and_return(false)
|
43
|
+
expect(screen.size_from_java).to eq(nil)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "imports java library on jruby" do
|
47
|
+
screen = TTY::Screen
|
48
|
+
class << screen
|
49
|
+
def java_import(*args); end
|
50
|
+
end
|
51
|
+
terminal = double(:terminal, get_height: 51, get_width: 211)
|
52
|
+
factory = double(:factory, get: terminal)
|
53
|
+
stub_const("TTY::Screen::TerminalFactory", factory)
|
54
|
+
|
55
|
+
allow(screen).to receive(:jruby?).and_return(true)
|
56
|
+
allow(screen).to receive(:require).with('java').and_return(true)
|
57
|
+
allow(screen).to receive(:java_import)
|
58
|
+
|
59
|
+
expect(screen.size_from_java).to eq([51, 211])
|
20
60
|
end
|
21
61
|
end
|
22
62
|
|
23
63
|
context 'from io console' do
|
24
64
|
it "doesn't calculate size if jruby " do
|
25
|
-
screen =
|
65
|
+
screen = TTY::Screen
|
26
66
|
allow(screen).to receive(:jruby?).and_return(true)
|
27
|
-
expect(screen.
|
67
|
+
expect(screen.size_from_io_console).to eq(nil)
|
28
68
|
end
|
29
69
|
|
30
70
|
it "calcualtes the size" do
|
31
|
-
screen =
|
71
|
+
screen = TTY::Screen
|
72
|
+
old_output = screen.output
|
73
|
+
screen.output = output
|
74
|
+
|
32
75
|
allow(screen).to receive(:jruby?).and_return(false)
|
33
76
|
allow(screen).to receive(:require).with('io/console').
|
34
77
|
and_return(true)
|
@@ -36,118 +79,146 @@ RSpec.describe TTY::Screen::Size, '#size' do
|
|
36
79
|
allow(IO).to receive(:method_defined?).with(:winsize).and_return(true)
|
37
80
|
allow(output).to receive(:winsize).and_return([100, 200])
|
38
81
|
|
39
|
-
expect(screen.
|
82
|
+
expect(screen.size_from_io_console).to eq([100, 200])
|
40
83
|
expect(output).to have_received(:winsize)
|
84
|
+
|
85
|
+
screen.output = old_output
|
41
86
|
end
|
42
87
|
|
43
88
|
it "doesn't calculate size if io/console not available" do
|
44
|
-
screen =
|
89
|
+
screen = TTY::Screen
|
45
90
|
allow(screen).to receive(:jruby?).and_return(false)
|
46
91
|
allow(screen).to receive(:require).with('io/console').
|
47
92
|
and_raise(LoadError)
|
48
|
-
expect(screen.
|
93
|
+
expect(screen.size_from_io_console).to eq(nil)
|
49
94
|
end
|
50
95
|
|
51
96
|
it "doesn't calculate size if it is run without a console" do
|
52
|
-
screen =
|
97
|
+
screen = TTY::Screen
|
53
98
|
allow(screen).to receive(:jruby?).and_return(false)
|
54
99
|
allow(screen).to receive(:require).with('io/console').
|
55
100
|
and_return(true)
|
56
|
-
allow(output).to receive(:tty?).and_return(true)
|
101
|
+
allow(screen.output).to receive(:tty?).and_return(true)
|
57
102
|
allow(IO).to receive(:method_defined?).with(:winsize).and_return(false)
|
58
|
-
expect(screen.
|
103
|
+
expect(screen.size_from_io_console).to eq(nil)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "from ioctl" do
|
108
|
+
it "reads terminal size" do
|
109
|
+
screen = TTY::Screen
|
110
|
+
old_output = screen.output
|
111
|
+
screen.output = output
|
112
|
+
allow(screen).to receive(:jruby?).and_return(false)
|
113
|
+
expect(screen.size_from_ioctl).to eq([51, 211])
|
114
|
+
screen.output = old_output
|
115
|
+
end
|
116
|
+
|
117
|
+
it "skips reading on jruby" do
|
118
|
+
allow(TTY::Screen).to receive(:jruby?).and_return(true)
|
119
|
+
expect(TTY::Screen.size_from_ioctl).to eq(nil)
|
59
120
|
end
|
60
121
|
end
|
61
122
|
|
62
123
|
context 'from tput' do
|
63
124
|
it "doesn't run command if outside of terminal" do
|
64
|
-
|
65
|
-
|
66
|
-
expect(screen.from_tput).to eq(nil)
|
125
|
+
allow(TTY::Screen.output).to receive(:tty?).and_return(false)
|
126
|
+
expect(TTY::Screen.size_from_tput).to eq(nil)
|
67
127
|
end
|
68
128
|
|
69
129
|
it "runs tput commands" do
|
70
|
-
screen =
|
71
|
-
allow(output).to receive(:tty?).and_return(true)
|
130
|
+
screen = TTY::Screen
|
131
|
+
allow(screen.output).to receive(:tty?).and_return(true)
|
72
132
|
allow(screen).to receive(:run_command).with('tput', 'lines').and_return(51)
|
73
133
|
allow(screen).to receive(:run_command).with('tput', 'cols').and_return(280)
|
74
|
-
expect(screen.
|
134
|
+
expect(screen.size_from_tput).to eq([51, 280])
|
75
135
|
end
|
76
136
|
|
77
137
|
it "doesn't return zero size" do
|
78
|
-
screen =
|
79
|
-
allow(output).to receive(:tty?).and_return(true)
|
138
|
+
screen = TTY::Screen
|
139
|
+
allow(screen.output).to receive(:tty?).and_return(true)
|
80
140
|
allow(screen).to receive(:run_command).with('tput', 'lines').and_return(0)
|
81
141
|
allow(screen).to receive(:run_command).with('tput', 'cols').and_return(0)
|
82
|
-
expect(screen.
|
142
|
+
expect(screen.size_from_tput).to eq(nil)
|
83
143
|
end
|
84
144
|
end
|
85
145
|
|
86
|
-
context 'from stty' do
|
146
|
+
context 'size from stty' do
|
87
147
|
it "doesn't run command if outside of terminal" do
|
88
|
-
|
89
|
-
|
90
|
-
expect(screen.from_stty).to eq(nil)
|
148
|
+
allow(TTY::Screen.output).to receive(:tty?).and_return(false)
|
149
|
+
expect(TTY::Screen.size_from_stty).to eq(nil)
|
91
150
|
end
|
92
151
|
|
93
152
|
it "runs stty commands" do
|
94
|
-
screen =
|
95
|
-
allow(output).to receive(:tty?).and_return(true)
|
153
|
+
screen = TTY::Screen
|
154
|
+
allow(screen.output).to receive(:tty?).and_return(true)
|
96
155
|
allow(screen).to receive(:run_command).with('stty', 'size').and_return("51 280")
|
97
|
-
expect(screen.
|
156
|
+
expect(screen.size_from_stty).to eq([51, 280])
|
98
157
|
end
|
99
158
|
|
100
159
|
it "doesn't return zero size" do
|
101
|
-
screen =
|
102
|
-
allow(output).to receive(:tty?).and_return(true)
|
160
|
+
screen = TTY::Screen
|
161
|
+
allow(screen.output).to receive(:tty?).and_return(true)
|
103
162
|
allow(screen).to receive(:run_command).with('stty', 'size').and_return("0 0")
|
104
|
-
expect(screen.
|
163
|
+
expect(screen.size_from_stty).to eq(nil)
|
105
164
|
end
|
106
165
|
end
|
107
166
|
|
108
|
-
context 'from env' do
|
167
|
+
context 'size from env' do
|
109
168
|
it "doesn't calculate size without COLUMNS key" do
|
110
|
-
screen =
|
111
|
-
|
169
|
+
screen = TTY::Screen
|
170
|
+
old_env = screen.env
|
171
|
+
screen.env = {'COLUMNS' => nil}
|
172
|
+
expect(screen.size_from_env).to eq(nil)
|
173
|
+
screen.env = old_env
|
112
174
|
end
|
113
175
|
|
114
176
|
it "extracts lines and columns from environment" do
|
115
|
-
screen =
|
116
|
-
|
177
|
+
screen = TTY::Screen
|
178
|
+
old_env = screen.env
|
179
|
+
screen.env = {'COLUMNS' => '280', 'LINES' => '51'}
|
180
|
+
expect(screen.size_from_env).to eq([51, 280])
|
181
|
+
screen.env = old_env
|
117
182
|
end
|
118
183
|
|
119
184
|
it "doesn't return zero size" do
|
120
|
-
screen =
|
121
|
-
|
185
|
+
screen = TTY::Screen
|
186
|
+
old_env = screen.env
|
187
|
+
screen.env = {'COLUMNS' => '0', 'LINES' => '0'}
|
188
|
+
expect(screen.size_from_env).to eq(nil)
|
189
|
+
screen.env = old_env
|
122
190
|
end
|
123
191
|
end
|
124
192
|
|
125
193
|
context 'from ansicon' do
|
126
194
|
it "doesn't calculate size without ANSICON key" do
|
127
|
-
screen =
|
128
|
-
|
195
|
+
screen = TTY::Screen
|
196
|
+
old_env = screen.env
|
197
|
+
screen.env = {'ANSICON' => nil}
|
198
|
+
expect(screen.size_from_ansicon).to eq(nil)
|
199
|
+
screen.env = old_env
|
129
200
|
end
|
130
201
|
|
131
202
|
it "extracts lines and columns from environment" do
|
132
|
-
screen =
|
133
|
-
|
203
|
+
screen = TTY::Screen
|
204
|
+
old_env = screen.env
|
205
|
+
screen.env = {'ANSICON' => '(280x51)'}
|
206
|
+
expect(screen.size_from_ansicon).to eq([51, 280])
|
207
|
+
screen.env = old_env
|
134
208
|
end
|
135
209
|
|
136
210
|
it "doesn't return zero size" do
|
137
|
-
screen =
|
138
|
-
|
211
|
+
screen = TTY::Screen
|
212
|
+
old_env = screen.env
|
213
|
+
screen.env = {'ANSICON' => '(0x0)'}
|
214
|
+
expect(screen.size_from_ansicon).to eq(nil)
|
215
|
+
screen.env = old_env
|
139
216
|
end
|
140
217
|
end
|
141
218
|
|
142
219
|
context 'default size' do
|
143
220
|
it "suggests default terminal size" do
|
144
|
-
|
145
|
-
expect(screen.default_size).to eq([27, 80])
|
146
|
-
end
|
147
|
-
|
148
|
-
it "attempts to get default terminal size from environment" do
|
149
|
-
screen = described_class.new({'LINES' => 52, 'COLUMNS' => 200})
|
150
|
-
expect(screen.default_size).to eq([52, 200])
|
221
|
+
expect(TTY::Screen::DEFAULT_SIZE).to eq([27, 80])
|
151
222
|
end
|
152
223
|
end
|
153
224
|
end
|
data/tasks/console.rake
CHANGED
data/tty-screen.gemspec
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'tty/
|
4
|
+
require 'tty/version'
|
5
5
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = 'tty-screen'
|
@@ -18,6 +18,8 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(spec)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
spec.required_ruby_version = '>= 2.0.0'
|
22
|
+
|
21
23
|
spec.add_development_dependency 'bundler', '>= 1.5.0', '< 2.0'
|
22
24
|
spec.add_development_dependency 'rake', '~> 10.0'
|
23
25
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tty-screen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Murach
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-10-
|
11
|
+
date: 2017-10-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -64,10 +64,9 @@ files:
|
|
64
64
|
- appveyor.yml
|
65
65
|
- lib/tty-screen.rb
|
66
66
|
- lib/tty/screen.rb
|
67
|
-
- lib/tty/
|
68
|
-
- lib/tty/screen/version.rb
|
67
|
+
- lib/tty/version.rb
|
69
68
|
- spec/spec_helper.rb
|
70
|
-
- spec/unit/
|
69
|
+
- spec/unit/height_width_spec.rb
|
71
70
|
- spec/unit/size_spec.rb
|
72
71
|
- tasks/console.rake
|
73
72
|
- tasks/coverage.rake
|
@@ -85,7 +84,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
85
84
|
requirements:
|
86
85
|
- - ">="
|
87
86
|
- !ruby/object:Gem::Version
|
88
|
-
version:
|
87
|
+
version: 2.0.0
|
89
88
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
89
|
requirements:
|
91
90
|
- - ">="
|
@@ -100,5 +99,5 @@ summary: Terminal screen size detection which works on Linux, OS X and Windows/C
|
|
100
99
|
platforms and supports MRI, JRuby and Rubinius interpreters.
|
101
100
|
test_files:
|
102
101
|
- spec/spec_helper.rb
|
103
|
-
- spec/unit/
|
102
|
+
- spec/unit/height_width_spec.rb
|
104
103
|
- spec/unit/size_spec.rb
|
data/lib/tty/screen/size.rb
DELETED
@@ -1,155 +0,0 @@
|
|
1
|
-
module TTY
|
2
|
-
class Screen
|
3
|
-
class Size
|
4
|
-
# Initialize terminal size detection
|
5
|
-
#
|
6
|
-
# @api public
|
7
|
-
def initialize(env, options = {})
|
8
|
-
@env = env
|
9
|
-
@output = options.fetch(:output) { $stderr }
|
10
|
-
@verbose = options.fetch(:verbose) { false }
|
11
|
-
end
|
12
|
-
|
13
|
-
# Get terminal rows and columns
|
14
|
-
#
|
15
|
-
# @return [Array[Integer, Integer]]
|
16
|
-
# return rows & columns
|
17
|
-
#
|
18
|
-
# @api public
|
19
|
-
def size
|
20
|
-
size = from_io_console
|
21
|
-
size ||= from_readline
|
22
|
-
size ||= from_tput
|
23
|
-
size ||= from_stty
|
24
|
-
size ||= from_env
|
25
|
-
size ||= from_ansicon
|
26
|
-
size || default_size
|
27
|
-
end
|
28
|
-
|
29
|
-
# Detect screen size by loading io/console lib
|
30
|
-
#
|
31
|
-
# @return [Array[Integer, Integer]]
|
32
|
-
#
|
33
|
-
# @api private
|
34
|
-
def from_io_console
|
35
|
-
return if jruby?
|
36
|
-
try_io_console { |size| size if nonzero_column?(size[1]) }
|
37
|
-
end
|
38
|
-
|
39
|
-
# Attempts to load native console extension
|
40
|
-
#
|
41
|
-
# @return [Boolean, Array]
|
42
|
-
#
|
43
|
-
# @api private
|
44
|
-
def try_io_console
|
45
|
-
require 'io/console'
|
46
|
-
|
47
|
-
begin
|
48
|
-
if output.tty? && IO.method_defined?(:winsize)
|
49
|
-
yield output.winsize
|
50
|
-
end
|
51
|
-
rescue Errno::EOPNOTSUPP
|
52
|
-
# no support for winsize on output
|
53
|
-
end
|
54
|
-
rescue LoadError
|
55
|
-
warn 'no native io/console support or io-console gem' if @verbose
|
56
|
-
end
|
57
|
-
|
58
|
-
# Detect screen size using Readline
|
59
|
-
#
|
60
|
-
# @api private
|
61
|
-
def from_readline
|
62
|
-
if defined?(Readline) && Readline.respond_to?(:get_screen_size)
|
63
|
-
size = Readline.get_screen_size
|
64
|
-
size if nonzero_column?(size[1])
|
65
|
-
end
|
66
|
-
rescue NotImplementedError
|
67
|
-
end
|
68
|
-
|
69
|
-
# Detect terminal size from tput utility
|
70
|
-
#
|
71
|
-
# @api private
|
72
|
-
def from_tput
|
73
|
-
return unless output.tty?
|
74
|
-
lines = run_command('tput', 'lines').to_i
|
75
|
-
cols = run_command('tput', 'cols').to_i
|
76
|
-
[lines, cols] if nonzero_column?(lines)
|
77
|
-
rescue Errno::ENOENT
|
78
|
-
end
|
79
|
-
|
80
|
-
# Detect terminal size from stty utility
|
81
|
-
#
|
82
|
-
# @api private
|
83
|
-
def from_stty
|
84
|
-
return unless output.tty?
|
85
|
-
out = run_command('stty', 'size')
|
86
|
-
return unless out
|
87
|
-
size = out.split.map(&:to_i)
|
88
|
-
size if nonzero_column?(size[1])
|
89
|
-
rescue Errno::ENOENT
|
90
|
-
end
|
91
|
-
|
92
|
-
# Detect terminal size from environment
|
93
|
-
#
|
94
|
-
# @api private
|
95
|
-
def from_env
|
96
|
-
return unless @env['COLUMNS'] =~ /^\d+$/
|
97
|
-
size = [(@env['LINES'] || @env['ROWS']).to_i, @env['COLUMNS'].to_i]
|
98
|
-
size if nonzero_column?(size[1])
|
99
|
-
end
|
100
|
-
|
101
|
-
# Detect terminal size on windows
|
102
|
-
#
|
103
|
-
# @api private
|
104
|
-
def from_ansicon
|
105
|
-
return unless @env['ANSICON'] =~ /\((.*)x(.*)\)/
|
106
|
-
size = [$2, $1].map(&:to_i)
|
107
|
-
size if nonzero_column?(size[1])
|
108
|
-
end
|
109
|
-
|
110
|
-
# Default terminal size
|
111
|
-
#
|
112
|
-
# @api public
|
113
|
-
def default_size
|
114
|
-
[
|
115
|
-
@env['LINES'].to_i.nonzero? || 27,
|
116
|
-
@env['COLUMNS'].to_i.nonzero? || 80
|
117
|
-
]
|
118
|
-
end
|
119
|
-
|
120
|
-
# Specifies an output stream object
|
121
|
-
#
|
122
|
-
# @api public
|
123
|
-
attr_reader :output
|
124
|
-
|
125
|
-
private
|
126
|
-
|
127
|
-
# Runs command silently capturing the output
|
128
|
-
#
|
129
|
-
# @api private
|
130
|
-
def run_command(*args)
|
131
|
-
require 'tempfile'
|
132
|
-
out = Tempfile.new('tty-screen')
|
133
|
-
result = system(*args, out: out.path)
|
134
|
-
return if result.nil?
|
135
|
-
out.rewind
|
136
|
-
out.read
|
137
|
-
ensure
|
138
|
-
out.close if out
|
139
|
-
end
|
140
|
-
|
141
|
-
# Check if number is non zero
|
142
|
-
#
|
143
|
-
# return [Boolean]
|
144
|
-
#
|
145
|
-
# @api private
|
146
|
-
def nonzero_column?(column)
|
147
|
-
column.to_i > 0
|
148
|
-
end
|
149
|
-
|
150
|
-
def jruby?
|
151
|
-
RbConfig::CONFIG['ruby_install_name'] == 'jruby'
|
152
|
-
end
|
153
|
-
end # Size
|
154
|
-
end # Screen
|
155
|
-
end # TTY
|
data/lib/tty/screen/version.rb
DELETED
data/spec/unit/new_spec.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
RSpec.describe TTY::Screen, '.new' do
|
2
|
-
|
3
|
-
it "initializses size with defaults" do
|
4
|
-
allow(TTY::Screen::Size).to receive(:new)
|
5
|
-
TTY::Screen.new
|
6
|
-
expect(TTY::Screen::Size).to have_received(:new).
|
7
|
-
with(ENV, {output: $stderr, verbose: false})
|
8
|
-
end
|
9
|
-
|
10
|
-
it "initializes size with values" do
|
11
|
-
allow(TTY::Screen::Size).to receive(:new)
|
12
|
-
|
13
|
-
TTY::Screen.new(output: :output, verbose: true)
|
14
|
-
|
15
|
-
expect(TTY::Screen::Size).to have_received(:new).
|
16
|
-
with(ENV, output: :output, verbose: true)
|
17
|
-
end
|
18
|
-
|
19
|
-
it "delegates size call" do
|
20
|
-
size_instance = spy(:size)
|
21
|
-
allow(TTY::Screen::Size).to receive(:new).and_return(size_instance)
|
22
|
-
|
23
|
-
screen = described_class.new
|
24
|
-
screen.size
|
25
|
-
|
26
|
-
expect(size_instance).to have_received(:size)
|
27
|
-
end
|
28
|
-
|
29
|
-
it "calls size" do
|
30
|
-
size_instance = double(:size, size: [51, 280])
|
31
|
-
allow(TTY::Screen::Size).to receive(:new).and_return(size_instance)
|
32
|
-
|
33
|
-
expect(TTY::Screen.size).to eq([51, 280])
|
34
|
-
expect(TTY::Screen::Size).to have_received(:new).once
|
35
|
-
end
|
36
|
-
|
37
|
-
it "calls width" do
|
38
|
-
size_instance = double(:size, size: [51, 280])
|
39
|
-
allow(TTY::Screen::Size).to receive(:new).and_return(size_instance)
|
40
|
-
|
41
|
-
expect(TTY::Screen.width).to eq(280)
|
42
|
-
expect(TTY::Screen::Size).to have_received(:new).once
|
43
|
-
end
|
44
|
-
|
45
|
-
it "calls height" do
|
46
|
-
size_instance = double(:size, size: [51, 280])
|
47
|
-
allow(TTY::Screen::Size).to receive(:new).and_return(size_instance)
|
48
|
-
expect(TTY::Screen.height).to eq(51)
|
49
|
-
end
|
50
|
-
end
|