ansi-select 0.1.0 → 0.2.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: dbdb0d626907ead9edbe0c70ece388639bfd60b2
4
- data.tar.gz: f6c5d41601a0ba70f4c5c14ba6b30992968fd6d6
3
+ metadata.gz: 31facd9c408f0836c2f8ab324fc26995701a433d
4
+ data.tar.gz: f6dc35cd7b1ea27db706eaba87912afe22abb5f9
5
5
  SHA512:
6
- metadata.gz: 93fd756748db4238b3fd331c947ae32973596909b04c349d40469688a1dcda566695c5109c1200514c4511f15d56c3e355d44fc3bd528abdd7e6df21a942a573
7
- data.tar.gz: beebfe8259573a84716765cbd55d0ac64e394bbb0f8a1aedb3c470972a5665b62070dbe4f3b377f5010fe6fc8756b3fe5d7111a601db708421bf5aa17d66de6b
6
+ metadata.gz: c13549de88f3ddc5905ea2813122140b1b0fec2a58a87a35c911e0fea365181867fa8fd2c8a06be1f4bd0b9668956aa267e3465f7d2e4a545d8b972cc4e2f14a
7
+ data.tar.gz: df044fe624e21523b04bccc32bf76e7565b055171d0cb946fbf84f68c30ceeecbe5637d2df1c88f20abf127e903e16d1b68653d9bf888c879c29079fed9b5395
data/CHANGELOG.md ADDED
@@ -0,0 +1,3 @@
1
+ ### 0.2.0
2
+
3
+ * Add multi select.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- ![](https://dl.dropboxusercontent.com/spa/dlqheu39w0arg9q/yaano147.png)
1
+ ![](https://dl.dropboxusercontent.com/spa/dlqheu39w0arg9q/gvpg7_fw.png)
2
2
 
3
3
  ## Installation
4
4
 
@@ -13,17 +13,25 @@ There are two options:
13
13
 
14
14
  ```bash
15
15
  echo some words to choose from | tr ' ' '\n' | ansi-select
16
+
16
17
  cd $(ls -d */ | ansi-select) # Go to a visually selected subdirectory.
17
18
  git checkout $(git branch | ansi-select) # The same, but with git branches.
19
+
20
+ rm -r $(ls | ansi-select --multi) # Delete all the selected files.
18
21
  ```
19
22
 
20
23
  * A Ruby library.
21
24
 
22
25
  ```ruby
23
- require "ansi/select"
26
+ require "ansi/selector"
27
+
28
+ beverage = Ansi::Selector.select(["coffee", "tee"])
29
+
30
+ puts "Would you like some additions?"
31
+ additions = Ansi::Selector.multi_select(["sugar", "cream", "milk"])
24
32
 
25
- answer = Ansi::Select.new(["some", "words", "to", "choose", "from"]).select
26
- print "You chose #{answer}."
33
+ print "Here's your #{beverage}. "
34
+ print "We've also added #{additions.join(', ')}." if additions.present?
27
35
  ```
28
36
 
29
37
  The Ruby interface has an additional benefit of accepting any objects that respond
@@ -32,10 +40,7 @@ to `#to_s` and returning one of them instead of a string.
32
40
 
33
41
  ## Keyboard
34
42
 
35
- You can use up and down keys or `j`/`k` for navigation, and space or return key for choosing an option.
36
- If you've changed your mind, you can quit with Ctrl+C or `q`.
37
-
38
-
39
- ## TODO
40
-
41
- * Support multi-select.
43
+ * Up and down arrows or `j`/`k` to move around.
44
+ * Return to choose.
45
+ * Space to toggle in multi select mode.
46
+ * Ctrl-c or `q` to quit.
data/ansi-select.gemspec CHANGED
@@ -1,16 +1,16 @@
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 'ansi/select/version'
4
+ require 'ansi/selector/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "ansi-select"
8
- spec.version = Ansi::Select::VERSION
8
+ spec.version = Ansi::Selector::VERSION
9
9
  spec.authors = ["Volodymyr Shatskyi"]
10
10
  spec.email = ["shockone89@gmail.com"]
11
11
 
12
- spec.summary = %q{Simple, not full-screen, ncurses-like TUI select}
13
- spec.description = %q{This gem allows you to select an array element (where an array is arbitrary input) with a pretty text user interface.}
12
+ spec.summary = %q{A simple, not full-screen, ncurses-like TUI selector}
13
+ spec.description = %q{This gem allows you to select array elements (where an array is some arbitrary input) with a pretty text user interface.}
14
14
  spec.homepage = "https://github.com/shockone/ansi-select"
15
15
 
16
16
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
data/bin/ansi-select CHANGED
@@ -1,4 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
- require 'ansi/select'
2
+ require 'ansi/selector'
3
3
 
4
- puts Ansi::Select.new(STDIN.readlines.map(&:chomp)).select
4
+ method_name = ARGV.include?('--multi') ? :multi_select : :select
5
+
6
+ puts Ansi::Selector.public_send(method_name, STDIN.readlines.map(&:chomp))
@@ -0,0 +1,21 @@
1
+ module Ansi
2
+ class Selector
3
+ # @param [Array<#to_s>] options
4
+ #
5
+ # @return [#to_s] option
6
+ def self.select(options)
7
+ require_relative "selector/single_impl"
8
+
9
+ SingleImpl.new(options).select
10
+ end
11
+
12
+ # @param [Array<#to_s>] options
13
+ #
14
+ # @return [Array<#to_s>] option
15
+ def self.multi_select(options)
16
+ require_relative "selector/multi_impl"
17
+
18
+ MultiImpl.new(options).select
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,133 @@
1
+ require "io/console"
2
+
3
+ module Ansi
4
+ class Selector
5
+ class Impl
6
+ CODES = {
7
+ standout_mode: `tput rev`,
8
+ exit_standout_mode: `tput rmso`,
9
+ cursor_up: `tput cuu1`,
10
+ cursor_down: `tput cud1`,
11
+ carriage_return_key: `tput cr`
12
+ }
13
+
14
+ def initialize(options)
15
+ @options = options
16
+
17
+ @highlighted_line_index = 0
18
+ @cursor_line_index = 0
19
+ end
20
+
21
+ def select
22
+ print_options
23
+ answer = ask_to_choose
24
+ go_to_line(@options.size)
25
+
26
+ answer
27
+ ensure
28
+ tty.close
29
+ end
30
+
31
+ private
32
+
33
+ # @return [File]
34
+ def tty
35
+ @tty ||= File.open('/dev/tty', 'w+')
36
+ end
37
+
38
+ def print_options
39
+ @options.each.with_index do |_, index|
40
+ print_line(index, index == @highlighted_line_index)
41
+
42
+ unless index == @options.size - 1
43
+ tty.print $/ # This strange thing is a cross-platform new line.
44
+ @cursor_line_index += 1
45
+ end
46
+ end
47
+
48
+ go_to_line(0)
49
+ end
50
+
51
+ # @return [String]
52
+ def listen_carefully_to_keyboard
53
+ tty.noecho do
54
+ tty.raw do
55
+ input = tty.getc.chr
56
+ if input == "\e"
57
+ input << tty.read_nonblock(3) rescue nil
58
+ input << tty.read_nonblock(2) rescue nil
59
+ end
60
+
61
+ input
62
+ end
63
+ end
64
+ end
65
+
66
+ # @return [#to_s]
67
+ def ask_to_choose
68
+ loop do
69
+ input = listen_carefully_to_keyboard
70
+
71
+ case input
72
+ when "\u0003", "q"
73
+ exit(0)
74
+ when " "
75
+ space_handler
76
+ when CODES[:carriage_return_key]
77
+ break carriage_return_handler
78
+ when "\e[A", "k", CODES[:cursor_up]
79
+ highlight_line(@highlighted_line_index - 1) unless @highlighted_line_index == 0
80
+ when "\e[B", "j", CODES[:cursor_down]
81
+ highlight_line(@highlighted_line_index + 1) unless @highlighted_line_index == @options.size - 1
82
+ end
83
+ end
84
+ end
85
+
86
+ # @param [Fixnum] index
87
+ # @param [Boolean] highlight
88
+ def print_line(index, highlight)
89
+ go_to_line(index)
90
+
91
+ if highlight
92
+ tty.print(CODES[:standout_mode] + prefix(index) + @options[index] + CODES[:exit_standout_mode])
93
+ else
94
+ tty.print(prefix(index) + @options[index])
95
+ end
96
+ end
97
+
98
+ # @param [Fixnum] index
99
+ def highlight_line(index)
100
+ print_line(@highlighted_line_index, false)
101
+ print_line(index, true)
102
+
103
+ @highlighted_line_index = index
104
+ end
105
+
106
+ # @param [Fixnum] index
107
+ def go_to_line(index)
108
+ if index == @cursor_line_index
109
+ # do nothing
110
+ elsif index > @cursor_line_index
111
+ (index - @cursor_line_index).times { tty.print CODES[:cursor_down] }
112
+ else
113
+ (@cursor_line_index - index).times { tty.print CODES[:cursor_up] }
114
+ end
115
+
116
+ @cursor_line_index = index
117
+ tty.print CODES[:carriage_return_key]
118
+ end
119
+
120
+ def prefix(index)
121
+ raise NotImplementedError
122
+ end
123
+
124
+ def space_handler
125
+ raise NotImplementedError
126
+ end
127
+
128
+ def carriage_return_handler
129
+ raise NotImplementedError
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,29 @@
1
+ require_relative 'impl'
2
+
3
+ module Ansi
4
+ class Selector
5
+ class MultiImpl < Impl
6
+ def initialize(options)
7
+ super
8
+
9
+ # @type [Array<Boolean>]
10
+ @selected_options = []
11
+ end
12
+
13
+ private
14
+
15
+ def prefix(index)
16
+ @selected_options[index] ? '[x] ' : '[ ] '
17
+ end
18
+
19
+ def space_handler
20
+ @selected_options[@cursor_line_index] = !@selected_options[@cursor_line_index]
21
+ print_line(@cursor_line_index, true)
22
+ end
23
+
24
+ def carriage_return_handler
25
+ @selected_options.map.with_index { |value, index| @options[index] if value }.compact
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ require_relative 'impl'
2
+
3
+ module Ansi
4
+ class Selector
5
+ class SingleImpl < Impl
6
+ private
7
+
8
+ def prefix(index)
9
+ ' '
10
+ end
11
+
12
+ def space_handler
13
+ # Do nothing
14
+ end
15
+
16
+ def carriage_return_handler
17
+ @options[@highlighted_line_index]
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,5 @@
1
+ module Ansi
2
+ class Selector
3
+ VERSION = "0.2.0"
4
+ end
5
+ end
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.1.0
4
+ version: 0.2.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-09-27 00:00:00.000000000 Z
11
+ date: 2015-10-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -38,8 +38,8 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '10.0'
41
- description: This gem allows you to select an array element (where an array is arbitrary
42
- input) with a pretty text user interface.
41
+ description: This gem allows you to select array elements (where an array is some
42
+ arbitrary input) with a pretty text user interface.
43
43
  email:
44
44
  - shockone89@gmail.com
45
45
  executables:
@@ -52,13 +52,17 @@ files:
52
52
  - ".ruby-gemset"
53
53
  - ".ruby-version"
54
54
  - ".travis.yml"
55
+ - CHANGELOG.md
55
56
  - Gemfile
56
57
  - README.md
57
58
  - Rakefile
58
59
  - ansi-select.gemspec
59
60
  - bin/ansi-select
60
- - lib/ansi/select.rb
61
- - lib/ansi/select/version.rb
61
+ - lib/ansi/selector.rb
62
+ - lib/ansi/selector/impl.rb
63
+ - lib/ansi/selector/multi_impl.rb
64
+ - lib/ansi/selector/single_impl.rb
65
+ - lib/ansi/selector/version.rb
62
66
  homepage: https://github.com/shockone/ansi-select
63
67
  licenses: []
64
68
  metadata: {}
@@ -81,5 +85,5 @@ rubyforge_project:
81
85
  rubygems_version: 2.4.8
82
86
  signing_key:
83
87
  specification_version: 4
84
- summary: Simple, not full-screen, ncurses-like TUI select
88
+ summary: A simple, not full-screen, ncurses-like TUI selector
85
89
  test_files: []
data/lib/ansi/select.rb DELETED
@@ -1,121 +0,0 @@
1
- # coding: utf-8
2
-
3
- require "io/console"
4
-
5
- module Ansi
6
- class Select
7
- CODES = {
8
- standout_mode: `tput rev`,
9
- exit_standout_mode: `tput rmso`,
10
- cursor_up: `tput cuu1`,
11
- cursor_down: `tput cud1`,
12
- carriage_return_key: `tput cr`
13
- }
14
-
15
- # @param [Array<#to_s>] options
16
- def initialize(options)
17
- @options = options
18
-
19
- @highlighted_line_index = 0
20
- @cursor_line_index = 0
21
- end
22
-
23
- # @return [#to_s] option
24
- def select
25
- print_options
26
- answer = ask_to_choose
27
- go_to_line(@options.size)
28
-
29
- answer
30
- ensure
31
- tty.close
32
- end
33
-
34
- private
35
-
36
- # @return [File]
37
- def tty
38
- @tty ||= File.open('/dev/tty', 'w+')
39
- end
40
-
41
- def print_options
42
- @options.each.with_index do |_, index|
43
- print_line(index, index == @highlighted_line_index)
44
-
45
- unless index == @options.size - 1
46
- tty.print $/ # This strange thing is a cross-platform new line.
47
- @cursor_line_index += 1
48
- end
49
- end
50
-
51
- go_to_line(0)
52
- end
53
-
54
- # @return [String]
55
- def listen_carefully_to_keyboard
56
- tty.noecho do
57
- tty.raw do
58
- input = tty.getc.chr
59
- if input == "\e"
60
- input << tty.read_nonblock(3) rescue nil
61
- input << tty.read_nonblock(2) rescue nil
62
- end
63
-
64
- input
65
- end
66
- end
67
- end
68
-
69
- # @return [#to_s]
70
- def ask_to_choose
71
- loop do
72
- input = listen_carefully_to_keyboard
73
-
74
- case input
75
- when "\u0003", "q"
76
- exit(0)
77
- when CODES[:carriage_return_key], " "
78
- break @options[@highlighted_line_index]
79
- when "\e[A", "k", CODES[:cursor_up]
80
- highlight_line(@highlighted_line_index - 1) unless @highlighted_line_index == 0
81
- when "\e[B", "j", CODES[:cursor_down]
82
- highlight_line(@highlighted_line_index + 1) unless @highlighted_line_index == @options.size - 1
83
- end
84
- end
85
- end
86
-
87
- # @param [Fixnum] index
88
- # @param [Boolean] highlight
89
- def print_line(index, highlight)
90
- go_to_line(index)
91
-
92
- if highlight
93
- tty.print "#{CODES[:standout_mode]}#{@options[index]}#{CODES[:exit_standout_mode]}"
94
- else
95
- tty.print @options[index]
96
- end
97
- end
98
-
99
- # @param [Fixnum] index
100
- def highlight_line(index)
101
- print_line(@highlighted_line_index, false)
102
- print_line(index, true)
103
-
104
- @highlighted_line_index = index
105
- end
106
-
107
- # @param [Fixnum] index
108
- def go_to_line(index)
109
- if index == @cursor_line_index
110
- # do nothing
111
- elsif index > @cursor_line_index
112
- (index - @cursor_line_index).times { tty.print CODES[:cursor_down] }
113
- else
114
- (@cursor_line_index - index).times { tty.print CODES[:cursor_up] }
115
- end
116
-
117
- @cursor_line_index = index
118
- tty.print CODES[:carriage_return_key]
119
- end
120
- end
121
- end
@@ -1,5 +0,0 @@
1
- module Ansi
2
- class Select
3
- VERSION = "0.1.0"
4
- end
5
- end