ansi-select 0.2.5 → 0.3.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: 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: {}