tty-screen 0.5.1 → 0.6.1

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: 0340d9961a71554c2a8a5cdef40f5c76bc56ec15
4
+ data.tar.gz: 63571f14e4a0bb74cb6f7812cf4893d9437c48de
5
5
  SHA512:
6
- metadata.gz: 9c7da830b7bfcc5c08f061d27e5ced6593fe39c26e503adee00364937531b7cee77a1eda6134eec8d344dd8e2fe412b28fd6de12edd56e37aa586ac401a453df
7
- data.tar.gz: 474a96f5cfe40c0d4cae44be6bbc928d2f350adc4aa96bfe32f399409f851b3dae50c266b135463410e7cc263f2146cad5bb726e0fd874a7c63dded3d5663b16
6
+ metadata.gz: 676e18ee572125b044a3a570c9921e0012c6c9b091782e55d11306fc99aa1fecc99c5b9782d5deee0ec3d1d9f2071669e5712d111c55856a856480d882a16249
7
+ data.tar.gz: 222b3b7e8fb05fbc737d587710039b93e65f8a4257cf3e4f329694bb7ad4d6920e4a63e58cae94c0b502f70b7a1e204cded0b454d960a449b3bef412dda71b8f
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Change log
2
2
 
3
+ ## [v0.6.1] - 2017-10-29
4
+
5
+ ### Fixed
6
+ * Fix #size_from_win_api to provide size if non zero to avoid [1,1] size
7
+
8
+ ## [v0.6.0] - 2017-10-29
9
+
10
+ ### Added
11
+ * Add #size_from_ioctl check for reading terminal size with Unix ioctl
12
+ * Add #size_from_java check for reading terminal size from Java on JRuby
13
+ * Add #size_from_win_api check for reading terminal size from Windows C API
14
+
15
+ ### Changed
16
+ * Change TTY::Screen to a module without any state
17
+ * Change to prefix all checks with `size` keyword
18
+ * Change gemspec to require ruby >= 2.0.0
19
+ * Remove #try_io_console and inline with io-console check
20
+ * Remove #default_size and replace with constant
21
+ * Remove TTY::Screen::Size class
22
+
3
23
  ## [v0.5.1] - 2017-10-26
4
24
 
5
25
  ### Changed
@@ -56,6 +76,8 @@
56
76
  ### Fixed
57
77
  * Fix bug with screen detection from_io_console by @luxflux
58
78
 
79
+ [v0.6.1]: https://github.com/peter-murach/tty-screen/compare/v0.6.0...v0.6.1
80
+ [v0.6.0]: https://github.com/peter-murach/tty-screen/compare/v0.5.1...v0.6.0
59
81
  [v0.5.1]: https://github.com/peter-murach/tty-screen/compare/v0.5.0...v0.5.1
60
82
  [v0.5.0]: https://github.com/peter-murach/tty-screen/compare/v0.4.3...v0.5.0
61
83
  [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 +1 @@
1
- require 'tty/screen'
1
+ require_relative 'tty/screen'
data/lib/tty/screen.rb CHANGED
@@ -1,66 +1,273 @@
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
+ size = [bottom - top + 1, right - left + 1]
97
+ return size if nonzero_column?(size[1] - 1)
98
+ rescue LoadError
99
+ warn 'no native fiddle module found' if verbose
100
+ rescue Fiddle::DLError
101
+ # non windows platform or no kernel32 lib
44
102
  end
103
+ module_function :size_from_win_api
45
104
 
46
- # Terminal lines count
105
+ # Determine terminal size on jruby using native Java libs
47
106
  #
48
- # @return [Integer]
107
+ # @return [nil, Array[Integer, Integer]]
49
108
  #
50
- # @api public
51
- def height
52
- size[0]
109
+ # @api private
110
+ def size_from_java(verbose: nil)
111
+ return unless jruby?
112
+ require 'java'
113
+ java_import 'jline.TerminalFactory'
114
+ terminal = TerminalFactory.get
115
+ [terminal.get_height, terminal.get_width]
116
+ rescue
117
+ warn 'failed to import java terminal package' if verbose
53
118
  end
54
- alias_method :rows, :height
119
+ module_function :size_from_java
55
120
 
56
- # Terminal columns count
121
+ # Detect screen size by loading io/console lib
57
122
  #
58
- # @return [Integer]
123
+ # On Windows io_console falls back to reading environment
124
+ # variables. This means any user changes to the terminal
125
+ # size won't be reflected in the runtime of the Ruby app.
59
126
  #
60
- # @api public
61
- def width
62
- size[1]
127
+ # @return [nil, Array[Integer, Integer]]
128
+ #
129
+ # @api private
130
+ def size_from_io_console(verbose: nil)
131
+ return if jruby?
132
+ require 'io/console'
133
+
134
+ begin
135
+ if @output.tty? && IO.method_defined?(:winsize)
136
+ size = @output.winsize
137
+ size if nonzero_column?(size[1])
138
+ end
139
+ rescue Errno::EOPNOTSUPP
140
+ # no support for winsize on output
141
+ end
142
+ rescue LoadError
143
+ warn 'no native io/console support or io-console gem' if verbose
144
+ end
145
+ module_function :size_from_io_console
146
+
147
+ TIOCGWINSZ = 0x5413
148
+ TIOCGWINSZ_PPC = 0x40087468
149
+
150
+ # Read terminal size from Unix ioctl
151
+ #
152
+ # @return [nil, Array[Integer, Integer]]
153
+ #
154
+ # @api private
155
+ def size_from_ioctl
156
+ return if jruby?
157
+ return unless @output.respond_to?(:ioctl)
158
+
159
+ format = 'SSSS'
160
+ buffer = ([0] * format.size).pack(format)
161
+ if ioctl?(TIOCGWINSZ, buffer) || ioctl?(TIOCGWINSZ_PPC, buffer)
162
+ rows, cols, = buffer.unpack(format)[0..1]
163
+ return [rows, cols]
164
+ end
165
+ end
166
+ module_function :size_from_ioctl
167
+
168
+ def ioctl?(control, buf)
169
+ @output.ioctl(control, buf) >= 0
170
+ rescue Errno::ENOTTY
171
+ # wrong processor architecture
172
+ false
173
+ rescue Errno::EINVAL
174
+ # ioctl failed to recognise processor type(not Intel)
175
+ false
176
+ end
177
+ module_function :ioctl?
178
+
179
+ # Detect screen size using Readline
180
+ #
181
+ # @api private
182
+ def size_from_readline
183
+ if defined?(Readline) && Readline.respond_to?(:get_screen_size)
184
+ size = Readline.get_screen_size
185
+ size if nonzero_column?(size[1])
186
+ end
187
+ rescue NotImplementedError
188
+ end
189
+ module_function :size_from_readline
190
+
191
+ # Detect terminal size from tput utility
192
+ #
193
+ # @api private
194
+ def size_from_tput
195
+ return unless @output.tty?
196
+ lines = run_command('tput', 'lines').to_i
197
+ cols = run_command('tput', 'cols').to_i
198
+ [lines, cols] if nonzero_column?(lines)
199
+ rescue Errno::ENOENT
200
+ end
201
+ module_function :size_from_tput
202
+
203
+ # Detect terminal size from stty utility
204
+ #
205
+ # @api private
206
+ def size_from_stty
207
+ return unless @output.tty?
208
+ out = run_command('stty', 'size')
209
+ return unless out
210
+ size = out.split.map(&:to_i)
211
+ size if nonzero_column?(size[1])
212
+ rescue Errno::ENOENT
213
+ end
214
+ module_function :size_from_stty
215
+
216
+ # Detect terminal size from environment
217
+ #
218
+ # After executing Ruby code if the user changes terminal
219
+ # dimensions during code runtime, the code won't be notified,
220
+ # and hence won't see the new dimensions reflected in its copy
221
+ # of LINES and COLUMNS environment variables.
222
+ #
223
+ # @return [nil, Array[Integer, Integer]]
224
+ #
225
+ # @api private
226
+ def size_from_env
227
+ return unless @env['COLUMNS'] =~ /^\d+$/
228
+ size = [(@env['LINES'] || @env['ROWS']).to_i, @env['COLUMNS'].to_i]
229
+ size if nonzero_column?(size[1])
230
+ end
231
+ module_function :size_from_env
232
+
233
+ # Detect terminal size from Windows ANSICON
234
+ #
235
+ # @api private
236
+ def size_from_ansicon
237
+ return unless @env['ANSICON'] =~ /\((.*)x(.*)\)/
238
+ size = [$2, $1].map(&:to_i)
239
+ size if nonzero_column?(size[1])
240
+ end
241
+ module_function :size_from_ansicon
242
+
243
+ # Runs command silently capturing the output
244
+ #
245
+ # @api private
246
+ def run_command(*args)
247
+ require 'tempfile'
248
+ out = Tempfile.new('tty-screen')
249
+ result = system(*args, out: out.path)
250
+ return if result.nil?
251
+ out.rewind
252
+ out.read
253
+ ensure
254
+ out.close if out
255
+ end
256
+ private_module_function :run_command
257
+
258
+ # Check if number is non zero
259
+ #
260
+ # return [Boolean]
261
+ #
262
+ # @api private
263
+ def nonzero_column?(column)
264
+ column.to_i > 0
265
+ end
266
+ private_module_function :nonzero_column?
267
+
268
+ def jruby?
269
+ RbConfig::CONFIG['ruby_install_name'] == 'jruby'
63
270
  end
64
- alias_method :columns, :width
271
+ private_module_function :jruby?
65
272
  end # Screen
66
273
  end # TTY
@@ -0,0 +1,5 @@
1
+ module TTY
2
+ module Screen
3
+ VERSION = "0.6.1"
4
+ end # Screen
5
+ end # TTY
@@ -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.1
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