ansi-select 0.2.5 → 0.3.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: d261e8c4cb86463bfba558e2c3104c30679d5a02
4
- data.tar.gz: 43851d45c17b1a4da2cce1da10952cceeea302ef
3
+ metadata.gz: e0b7678dcd7b0eb448dc1ffa8cd3ee7621c89093
4
+ data.tar.gz: 4e7f2a784d81aa8c3e488715ab795381ec87fcd1
5
5
  SHA512:
6
- metadata.gz: 691c63cfcfad6589ad5d423e8e8fb3b6c204054a998a841728c5bb63dfd8260296fcfc09f5094f4e879fc0ba881cdc7c7a2fd08fbb870fd6a56d0c3e21159852
7
- data.tar.gz: 3f5777142597d125447659e280377941481d78559863b12f5180df015de50e58c417ad02c9af6e6521a5c3daec06f68ba06f58b55153019267031089b569c758
6
+ metadata.gz: 008e8fa95ec62bfcf6ebb7233b3b4f6cc360fe3ef275ce3f515d4955fef24783bc92e589ff1d1b3c6ed694c4c08c64e0f353786c4f84fa7d62fbfbd8706ae088
7
+ data.tar.gz: 709585d4347c5398ac9030d4566f8f5fe6827aeca847fe065778f776c47876754219e62c4eca4ff87749eb541a32ce6bea7b0585c93ab5d36ef3e0752f87e7cb
@@ -1,3 +1,10 @@
1
+ ### 0.3.0
2
+
3
+ * Fix default formatter not defined error.
4
+ * Add ellipsis to options that don't fit into the screen.
5
+ * Re-render on terminal resize.
6
+ * Scroll if options don't fit into the terminal.
7
+
1
8
  ### 0.2.5
2
9
 
3
10
  * Allow to preselect options.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
- require 'ansi/selector'
2
+ require_relative '../lib/ansi/selector'
3
3
 
4
4
  method_name = ARGV.include?('--multi') ? :multi_select : :select
5
5
 
@@ -1,32 +1,34 @@
1
1
  module Ansi
2
2
  class Selector
3
- # @param [Array<Object>] options
4
- # @param [Proc] formatter
5
- # @param [Fixnum] preselected
6
- #
7
- # @return [Object] option
8
- def self.select(options, formatter: default_formatter, preselected: 0)
9
- require_relative "selector/single_impl"
3
+ class << self
4
+ # @param [Array<Object>] options
5
+ # @param [Proc] formatter
6
+ # @param [Fixnum] preselected
7
+ #
8
+ # @return [Object] option
9
+ def select(options, formatter: default_formatter, preselected: 0)
10
+ require_relative "selector/single_impl"
10
11
 
11
- SingleImpl.new(options, formatter, preselected).select
12
- end
12
+ SingleImpl.new(options, formatter, preselected).select
13
+ end
13
14
 
14
- # @param [Array<Object>] options
15
- # @param [Proc] formatter
16
- # @param [Array<Fixnum>] preselected
17
- #
18
- # @return [Array<Object>] option
19
- def self.multi_select(options, formatter: default_formatter, preselected: [])
20
- require_relative "selector/multi_impl"
15
+ # @param [Array<Object>] options
16
+ # @param [Proc] formatter
17
+ # @param [Array<Fixnum>] preselected
18
+ #
19
+ # @return [Array<Object>] option
20
+ def multi_select(options, formatter: default_formatter, preselected: [])
21
+ require_relative "selector/multi_impl"
21
22
 
22
- MultiImpl.new(options, formatter, preselected).select
23
- end
23
+ MultiImpl.new(options, formatter, preselected).select
24
+ end
24
25
 
25
- private
26
+ private
26
27
 
27
- # @return [Proc]
28
- def default_formatter
29
- ->(option) { option.name }
28
+ # @return [Proc]
29
+ def default_formatter
30
+ :to_s.to_proc
31
+ end
30
32
  end
31
33
  end
32
34
  end
@@ -16,20 +16,54 @@ module Ansi
16
16
  @formatter = formatter
17
17
  @highlighted_line_index = 0
18
18
  @cursor_line_index = 0
19
+ @columns = terminal_columns
19
20
 
20
21
  # Converts options to column-aligned strings.
21
- formatted = @options.map(&@formatter).map(&method(:Array))
22
+ formatted = @options.map(&@formatter).map(&method(:Array)).map { |line| line.map(&method(:strip_ansi_colors)) }
22
23
  @formatted ||= formatted.map do |f|
23
24
  f.map.with_index do |part, column|
24
25
  width = formatted.map { |fm| fm[column] }.compact.map(&:size).max
25
26
  part.to_s.ljust(width)
26
27
  end.join(' ')
27
28
  end
29
+
30
+ Signal.trap('SIGWINCH', proc do
31
+ columns = terminal_columns
32
+ if columns < @columns && !all_options_fit?(columns)
33
+ # When a buffer gets narrower, the text that doesn't fit into the screen anymore
34
+ # starts jumping around in a way that makes it hard to predict. In such case we
35
+ # clear everything and re-print options at the beginning of the buffer to
36
+ # simplify things.
37
+ tty.print(`printf '\e[2J'`)
38
+ tty.print(`tput cup 0 0`)
39
+ else
40
+ print_line(0)
41
+ end
42
+
43
+ @columns = columns
44
+ print_options
45
+ end)
46
+ end
47
+
48
+ def all_options_fit?(columns)
49
+ option_indices.map(&method(:final_text_for_line)).map(&:size).all? { |size| size < columns }
50
+ end
51
+
52
+ # @return [Fixnum]
53
+ def terminal_columns
54
+ `tput cols`.to_i
55
+ end
56
+
57
+ # @return [Fixnum]
58
+ def terminal_lines
59
+ `tput lines`.to_i
28
60
  end
29
61
 
30
62
  def select
31
63
  print_options
32
64
  answer = ask_to_choose
65
+ # We need to reprint here in order to render the lines that are behind the buffer.
66
+ range(@highlighted_line_index, @options.size - 1).each(&method(:print_line))
33
67
  go_to_line(@options.size)
34
68
 
35
69
  answer
@@ -45,16 +79,16 @@ module Ansi
45
79
  end
46
80
 
47
81
  def print_options
48
- @options.each.with_index do |_, index|
49
- print_line(index, index == @highlighted_line_index)
82
+ @options[0...terminal_lines].each.with_index do |_, index|
83
+ print_line(index)
50
84
 
51
85
  unless index == @options.size - 1
52
- tty.print $/ # This strange thing is a cross-platform new line.
86
+ tty.print $/ # This global variable is a cross-platform new line.
53
87
  @cursor_line_index += 1
54
88
  end
55
89
  end
56
90
 
57
- go_to_line(0)
91
+ print_line(0)
58
92
  end
59
93
 
60
94
  # @return [String]
@@ -81,38 +115,36 @@ module Ansi
81
115
  when "\u0003", "q"
82
116
  exit(0)
83
117
  when " "
84
- space_handler
118
+ space_key_handler
85
119
  when CODES[:carriage_return_key]
86
120
  break carriage_return_handler
87
121
  when "\e[A", "k", CODES[:cursor_up]
88
- highlight_line(@highlighted_line_index - 1) unless @highlighted_line_index == 0
122
+ scroll_to(@highlighted_line_index - 1)
89
123
  when "\e[B", "j", CODES[:cursor_down]
90
- highlight_line(@highlighted_line_index + 1) unless @highlighted_line_index == @options.size - 1
124
+ scroll_to(@highlighted_line_index + 1)
91
125
  end
92
126
  end
93
127
  end
94
128
 
95
129
  # @param [Fixnum] index
96
- # @param [Boolean] highlight
97
- def print_line(index, highlight)
130
+ def scroll_to(index)
131
+ indices_to_reprint = range(@highlighted_line_index, index)
132
+ @highlighted_line_index = indices_to_reprint.last
133
+ indices_to_reprint.each(&method(:print_line))
134
+ end
135
+
136
+ # @param [Fixnum] index
137
+ def print_line(index)
98
138
  go_to_line(index)
99
- text = prefix(index) + @formatted[index]
139
+ text = final_text_for_line(index)
100
140
 
101
- if highlight
141
+ if index == @highlighted_line_index
102
142
  tty.print(CODES[:standout_mode] + text + CODES[:exit_standout_mode])
103
143
  else
104
144
  tty.print(text)
105
145
  end
106
146
  end
107
147
 
108
- # @param [Fixnum] index
109
- def highlight_line(index)
110
- print_line(@highlighted_line_index, false)
111
- print_line(index, true)
112
-
113
- @highlighted_line_index = index
114
- end
115
-
116
148
  # @param [Fixnum] index
117
149
  def go_to_line(index)
118
150
  if index == @cursor_line_index
@@ -120,24 +152,55 @@ module Ansi
120
152
  elsif index > @cursor_line_index
121
153
  (index - @cursor_line_index).times { tty.print CODES[:cursor_down] }
122
154
  else
123
- (@cursor_line_index - index).times { tty.print CODES[:cursor_up] }
155
+ row_before_going = `bash position.sh`.to_i
156
+ (@cursor_line_index - index).times do |step|
157
+ tty.print(`tput ri`) if step >= row_before_going
158
+ tty.print CODES[:cursor_up]
159
+ end
124
160
  end
125
161
 
126
162
  @cursor_line_index = index
127
163
  tty.print CODES[:carriage_return_key]
128
164
  end
129
165
 
166
+ # @param [Fixnum] index
167
+ #
168
+ # @return [String]
169
+ def final_text_for_line(index)
170
+ maybe_add_ellipsis(prefix(index) + @formatted[index])
171
+ end
172
+
173
+ # @param [String] text
174
+ def maybe_add_ellipsis(text)
175
+ text.size >= @columns ? text[0...(@columns - 2)] + '…' : text
176
+ end
177
+
178
+ # @param [String] text
179
+ #
180
+ # @return [String]
181
+ def strip_ansi_colors(text)
182
+ text.gsub(/\e\[(\d+;?)+m/, '')
183
+ end
184
+
130
185
  def prefix(index)
131
186
  raise NotImplementedError
132
187
  end
133
188
 
134
- def space_handler
189
+ def space_key_handler
135
190
  raise NotImplementedError
136
191
  end
137
192
 
138
193
  def carriage_return_handler
139
194
  raise NotImplementedError
140
195
  end
196
+
197
+ def option_indices
198
+ (0...@options.size)
199
+ end
200
+
201
+ def range(from, to)
202
+ ((from <= to) ? from.upto(to) : from.downto(to)).to_a.select { |index| (0...@options.size).cover?(index) }
203
+ end
141
204
  end
142
205
  end
143
206
  end
@@ -14,9 +14,9 @@ module Ansi
14
14
  @selected_options[index] ? ' [x] ' : ' [ ] '
15
15
  end
16
16
 
17
- def space_handler
17
+ def space_key_handler
18
18
  @selected_options[@cursor_line_index] = !@selected_options[@cursor_line_index]
19
- print_line(@cursor_line_index, true)
19
+ print_line(@cursor_line_index)
20
20
  end
21
21
 
22
22
  def carriage_return_handler
@@ -14,7 +14,7 @@ module Ansi
14
14
  ' '
15
15
  end
16
16
 
17
- def space_handler
17
+ def space_key_handler
18
18
  # Do nothing
19
19
  end
20
20
 
@@ -1,5 +1,5 @@
1
1
  module Ansi
2
2
  class Selector
3
- VERSION = "0.2.5"
3
+ VERSION = "0.3.0"
4
4
  end
5
5
  end
@@ -0,0 +1,14 @@
1
+ #!/bin/bash
2
+ # based on a script from http://invisible-island.net/xterm/xterm.faq.html
3
+ exec < /dev/tty
4
+ oldstty=$(stty -g)
5
+ stty raw -echo min 0
6
+ # on my system, the following line can be replaced by the line below it
7
+ echo -en "\033[6n" > /dev/tty
8
+ # tput u7 > /dev/tty # when TERM=xterm (and relatives)
9
+ IFS=';' read -r -d R -a pos
10
+ stty $oldstty
11
+ # change from one-based to zero based so they work with: tput cup $row $col
12
+ row=$((${pos[0]:2} - 1)) # strip off the esc-[
13
+
14
+ echo "$row"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ansi-select
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Volodymyr Shatskyi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-09 00:00:00.000000000 Z
11
+ date: 2015-10-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -63,6 +63,7 @@ files:
63
63
  - lib/ansi/selector/multi_impl.rb
64
64
  - lib/ansi/selector/single_impl.rb
65
65
  - lib/ansi/selector/version.rb
66
+ - position.sh
66
67
  homepage: https://github.com/shockone/ansi-select
67
68
  licenses: []
68
69
  metadata: {}