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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9a63a2019e73caf56b233666335b21ec5f0f5200
4
- data.tar.gz: c01de14e847e44852e352a7a916379f82c657560
3
+ metadata.gz: dae2d40de8e2675bc91d26ec698d333bce317169
4
+ data.tar.gz: 2b26bc3794b5a6b05ff230b9202a11b59c60a8f3
5
5
  SHA512:
6
- metadata.gz: 9c7da830b7bfcc5c08f061d27e5ced6593fe39c26e503adee00364937531b7cee77a1eda6134eec8d344dd8e2fe412b28fd6de12edd56e37aa586ac401a453df
7
- data.tar.gz: 474a96f5cfe40c0d4cae44be6bbc928d2f350adc4aa96bfe32f399409f851b3dae50c266b135463410e7cc263f2146cad5bb726e0fd874a7c63dded3d5663b16
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
- screen = TTY::Screen.new
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
- You can also use above methods as class instance methods:
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
- require_relative 'screen/size'
2
- require_relative 'screen/version'
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
- class Screen
9
- # Create terminal screen
10
- #
11
- # @param [Hash] options
12
- # @option options [Object] :output
13
- # the object that responds to print call defaulting to stderr
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
- def initialize(options = {})
17
- @output = options.fetch(:output) { $stderr }
18
- @verbose = options.fetch(:verbose) { false }
19
- @size = Size.new(ENV, output: @output, verbose: @verbose)
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 self.size
24
- new.size
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
- # @api public
28
- def self.width
53
+ def width
29
54
  size[1]
30
55
  end
56
+ module_function :width
31
57
 
32
- # @api public
33
- def self.height
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
- # Terminal size as tuple
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 public
42
- def size
43
- @size.size
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
- # Terminal lines count
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 public
51
- def height
52
- size[0]
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
- alias_method :rows, :height
118
+ module_function :size_from_java
55
119
 
56
- # Terminal columns count
120
+ # Detect screen size by loading io/console lib
57
121
  #
58
- # @return [Integer]
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
- # @api public
61
- def width
62
- size[1]
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
- alias_method :columns, :width
270
+ private_module_function :jruby?
65
271
  end # Screen
66
272
  end # TTY
@@ -0,0 +1,5 @@
1
+ module TTY
2
+ module Screen
3
+ VERSION = "0.6.0"
4
+ end # Screen
5
+ end # TTY
data/lib/tty-screen.rb CHANGED
@@ -1 +1 @@
1
- require 'tty/screen'
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
@@ -1,34 +1,77 @@
1
1
  require 'delegate'
2
2
 
3
- RSpec.describe TTY::Screen::Size, '#size' do
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 = described_class.new({}, output: output)
15
- allow(screen).to receive(:from_io_console).and_return([51, 280])
16
- allow(screen).to receive(:from_readline).and_return(nil)
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).to_not have_received(:from_readline)
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 = described_class.new({})
65
+ screen = TTY::Screen
26
66
  allow(screen).to receive(:jruby?).and_return(true)
27
- expect(screen.from_io_console).to eq(nil)
67
+ expect(screen.size_from_io_console).to eq(nil)
28
68
  end
29
69
 
30
70
  it "calcualtes the size" do
31
- screen = described_class.new({}, output: output)
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.from_io_console).to eq([100, 200])
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 = described_class.new({})
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.from_io_console).to eq(nil)
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 = described_class.new({}, output: output)
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.from_io_console).to eq(nil)
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
- screen = described_class.new({}, output: output)
65
- allow(output).to receive(:tty?).and_return(false)
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 = described_class.new({}, output: output)
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.from_tput).to eq([51, 280])
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 = described_class.new({}, output: output)
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.from_tput).to eq(nil)
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
- screen = described_class.new({}, output: output)
89
- allow(output).to receive(:tty?).and_return(false)
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 = described_class.new({}, output: output)
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.from_stty).to eq([51, 280])
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 = described_class.new({}, output: output)
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.from_stty).to eq(nil)
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 = described_class.new({'COLUMNS' => nil})
111
- expect(screen.from_env).to eq(nil)
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 = described_class.new({'COLUMNS' => '280', 'LINES' => '51'})
116
- expect(screen.from_env).to eq([51, 280])
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 = described_class.new({'COLUMNS' => '0', 'LINES' => '0'})
121
- expect(screen.from_env).to eq(nil)
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 = described_class.new({'ANSICON' => nil})
128
- expect(screen.from_ansicon).to eq(nil)
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 = described_class.new({'ANSICON' => '(280x51)'})
133
- expect(screen.from_ansicon).to eq([51, 280])
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 = described_class.new({'ANSICON' => '(0x0)'})
138
- expect(screen.from_ansicon).to eq(nil)
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
- screen = described_class.new({'LINES' => 0, 'COLUMNS' => 0})
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
@@ -8,3 +8,4 @@ task :console do
8
8
  ARGV.clear
9
9
  IRB.start
10
10
  end
11
+ task :c => :console
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/screen/version'
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.5.1
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-26 00:00:00.000000000 Z
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/screen/size.rb
68
- - lib/tty/screen/version.rb
67
+ - lib/tty/version.rb
69
68
  - spec/spec_helper.rb
70
- - spec/unit/new_spec.rb
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: '0'
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/new_spec.rb
102
+ - spec/unit/height_width_spec.rb
104
103
  - spec/unit/size_spec.rb
@@ -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
@@ -1,5 +0,0 @@
1
- module TTY
2
- class Screen
3
- VERSION = "0.5.1"
4
- end # Screen
5
- end # TTY
@@ -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