tty-screen 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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