madCLIbs 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/Gemfile +9 -0
- data/LICENSE.txt +26 -0
- data/README.md +28 -0
- data/Rakefile +2 -0
- data/examples/basic.rb +6 -0
- data/examples/colors.rb +17 -0
- data/examples/scoped.rb +13 -0
- data/lib/madCLIbs/import.rb +10 -0
- data/lib/madCLIbs.rb +3 -0
- data/lib/mad_clibs/blanks/base.rb +43 -0
- data/lib/mad_clibs/blanks/mixins/color_handling.rb +50 -0
- data/lib/mad_clibs/blanks/mixins/default_key_handling.rb +35 -0
- data/lib/mad_clibs/blanks/mixins/value_buffer_delegate.rb +34 -0
- data/lib/mad_clibs/blanks/string.rb +23 -0
- data/lib/mad_clibs/blanks.rb +3 -0
- data/lib/mad_clibs/line.rb +66 -0
- data/lib/mad_clibs/prompter.rb +105 -0
- data/lib/mad_clibs/util/iohelper.rb +134 -0
- data/lib/mad_clibs/util/value_buffer.rb +37 -0
- data/lib/mad_clibs/version.rb +3 -0
- data/lib/mad_clibs.rb +19 -0
- data/madCLIbs.gemspec +31 -0
- metadata +101 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0c3ce55fec8d9b38d33c8eb9d4d716281c1b6bc2
|
4
|
+
data.tar.gz: 75dfe916d9f51d70a2c521aff89c1360772422ac
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: ae10344a49778297444e83391b9e681e0d95e1dd2eb37efcb0b2c016c01bceaa1bb1313716b8fae0649736051e8c0a49b8f975e67eff65fdd19106253e10f4cf
|
7
|
+
data.tar.gz: 476a18157930971186915a5c57a548adb4d21740c89bfaeaa2509375e6148d0f3425864a8fc2d88b3786deb93e081b308cc3e5d9478de283d66b273f5a0909e5
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
Except where indicated in header comments, this work is
|
2
|
+
|
3
|
+
Copyright (c) 2015 Donald Guy
|
4
|
+
|
5
|
+
and licensed under the terms of the
|
6
|
+
|
7
|
+
MIT License
|
8
|
+
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
10
|
+
a copy of this software and associated documentation files (the
|
11
|
+
"Software"), to deal in the Software without restriction, including
|
12
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
13
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
14
|
+
permit persons to whom the Software is furnished to do so, subject to
|
15
|
+
the following conditions:
|
16
|
+
|
17
|
+
The above copyright notice and this permission notice shall be
|
18
|
+
included in all copies or substantial portions of the Software.
|
19
|
+
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
21
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
22
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
23
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
24
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
25
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
26
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# MadCLI</tt>bs:<br> User-Friendly Fill-in-the-Blank(s) UX for your Terminal Application
|
2
|
+
|
3
|
+
Seeks to mimic the familiar UX of HTML5 form elements, with
|
4
|
+
[placeholders](http://diveintohtml5.info/forms.html#placeholder),
|
5
|
+
in the terminal.
|
6
|
+
|
7
|
+
Allows a program to present a line with one or more "blanks" which can be edited and moved between
|
8
|
+
at will (with tab or up & down arrow keys). On new line, returns the values of the blanks (treating
|
9
|
+
placeholders as a default values)
|
10
|
+
|
11
|
+
The name is supposed to be Mad Libs with a CLI in it, but you might also
|
12
|
+
reasonably call it "Mad CLI BS" :smile:
|
13
|
+
|
14
|
+
You could, in fact, use it to make a sweet Mad Libs app, but its also
|
15
|
+
pretty useful for things like [Dramaturg](https://github.com/donaldguy/dramaturg)
|
16
|
+
(for which it was created)
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
See [examples](https://github.com/donaldguy/madCLIbs/tree/master/examples)
|
21
|
+
|
22
|
+
## Contributing
|
23
|
+
|
24
|
+
1. Fork it ( https://github.com/donaldguy/madCLIbs/fork )
|
25
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
26
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
27
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
28
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/examples/basic.rb
ADDED
data/examples/colors.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'madCLIbs/import'
|
2
|
+
require 'term/ansicolor'
|
3
|
+
|
4
|
+
include Term::ANSIColor
|
5
|
+
|
6
|
+
prompt(red("Tokens"), string("are"), red("styled"), string(green(bold("individually"))))
|
7
|
+
|
8
|
+
prompt("Placeholders can have", cyan(string("the same style as a typed value")))
|
9
|
+
|
10
|
+
prompt("Or a", red(string(cyan("different one"))))
|
11
|
+
prompt(green("Including"), red(string(reset("none"))))
|
12
|
+
|
13
|
+
prompt(bold, "You can also used unscoped colors", red,
|
14
|
+
string(cyan("but")), "they leak til next reset", reset )
|
15
|
+
|
16
|
+
#you cannot/should not try to wrap multiple tokens in a scope tho
|
17
|
+
# none of this: prompt( bold("Question":, string("answer") ) )
|
data/examples/scoped.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'madCLIbs'
|
2
|
+
require 'term/ansicolor'
|
3
|
+
|
4
|
+
$cli = MadCLIbs.new
|
5
|
+
$color = Term::ANSIColor
|
6
|
+
|
7
|
+
s1, s2 = $cli.prompt($color.bold,
|
8
|
+
"Should also work without",
|
9
|
+
$color.cyan($cli.string("polluting too many")),
|
10
|
+
$color.red($cli.string("namespaces")),
|
11
|
+
$color.reset)
|
12
|
+
|
13
|
+
puts "Field values s1=#{s1}, s2=#{s2}"
|
data/lib/madCLIbs.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative 'mixins/color_handling'
|
2
|
+
require_relative 'mixins/default_key_handling'
|
3
|
+
|
4
|
+
class MadClibs
|
5
|
+
module Blanks
|
6
|
+
class Base
|
7
|
+
|
8
|
+
attr_reader :start_value, :completions
|
9
|
+
attr_accessor :value, :position
|
10
|
+
def length; self.value.length; end
|
11
|
+
def empty?; self.value.length <= 0; end
|
12
|
+
|
13
|
+
def render
|
14
|
+
raise NotImplementedEror, "render called on abstract superclass D:"
|
15
|
+
end
|
16
|
+
|
17
|
+
def post_render
|
18
|
+
true # no-op
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid?
|
22
|
+
raise NotImplementedEror, "valid? called on abstract superclass D:"
|
23
|
+
end
|
24
|
+
|
25
|
+
def placeholder_showing?
|
26
|
+
@placeholder_showing = true unless defined? @placeholder_showing
|
27
|
+
@placeholder_showing
|
28
|
+
end
|
29
|
+
|
30
|
+
def key(key)
|
31
|
+
if placeholder_showing?
|
32
|
+
default_key_placeholder_showing(key)
|
33
|
+
else #no placeholder showing
|
34
|
+
default_key_no_placeholder(key)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
protected
|
39
|
+
include Mixins::ColorHandling
|
40
|
+
include Mixins::DefaultKeyHandling
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'term/ansicolor'
|
2
|
+
|
3
|
+
module Term::ANSIColor
|
4
|
+
alias old_color color
|
5
|
+
def color(name, string = nil, &block)
|
6
|
+
if string.respond_to? :save_and_remove_color
|
7
|
+
string.send :save_and_remove_color, old_color(name, string.value)
|
8
|
+
string
|
9
|
+
else
|
10
|
+
old_color(name, string, &block)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class MadClibs
|
16
|
+
module Blanks::Mixins
|
17
|
+
module ColorHandling
|
18
|
+
include Term::ANSIColor
|
19
|
+
|
20
|
+
def save_and_remove_start_color(s)
|
21
|
+
@colored_placeholder = s
|
22
|
+
stripped = uncolor(s)
|
23
|
+
|
24
|
+
@placeholder_inherits_style = (@colored_placeholder == stripped)
|
25
|
+
stripped
|
26
|
+
end
|
27
|
+
|
28
|
+
def save_and_remove_color(s)
|
29
|
+
colored = s
|
30
|
+
uncolored = uncolor(s)
|
31
|
+
@color_seed = colored.sub(%r'#{uncolored}', '%{value}')
|
32
|
+
uncolored
|
33
|
+
end
|
34
|
+
|
35
|
+
def restore_color(v)
|
36
|
+
if placeholder_showing? and !@placeholder_inherits_style
|
37
|
+
@colored_placeholder
|
38
|
+
elsif @color_seed
|
39
|
+
@color_seed % {value: v}
|
40
|
+
else
|
41
|
+
value
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_str
|
46
|
+
value
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class MadClibs
|
2
|
+
module Blanks::Mixins::DefaultKeyHandling
|
3
|
+
def default_key_placeholder_showing(key)
|
4
|
+
case key
|
5
|
+
when"backspace"
|
6
|
+
#toggle visible display of value; leave behavior the same
|
7
|
+
#whether placeholder visible or not
|
8
|
+
if self.value == start_value
|
9
|
+
self.value = ""
|
10
|
+
else
|
11
|
+
self.value = start_value
|
12
|
+
end
|
13
|
+
when "left", "right"
|
14
|
+
#ignore it
|
15
|
+
else # normal character -> start writing in
|
16
|
+
self.value = ""
|
17
|
+
value_key(key)
|
18
|
+
@placeholder_showing = false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def default_key_no_placeholder(key)
|
23
|
+
case key
|
24
|
+
when"backspace"
|
25
|
+
value_key(key)
|
26
|
+
if self.value.empty?
|
27
|
+
self.value = start_value
|
28
|
+
@placeholder_showing = true
|
29
|
+
end
|
30
|
+
else
|
31
|
+
value_key(key)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require_relative '../../util/value_buffer'
|
2
|
+
|
3
|
+
class MadClibs
|
4
|
+
module Blanks::Mixins::ValueBufferDelegate
|
5
|
+
def value
|
6
|
+
@valuebuff ||= ValueBuffer.new(save_and_remove_start_color(start_value))
|
7
|
+
@valuebuff.value
|
8
|
+
end
|
9
|
+
|
10
|
+
def value=(v)
|
11
|
+
@valuebuff ||= ValueBuffer.new(save_and_remove_color(v))
|
12
|
+
@valuebuff.value = v
|
13
|
+
end
|
14
|
+
|
15
|
+
def value_key(key)
|
16
|
+
case key
|
17
|
+
when "backspace"
|
18
|
+
@valuebuff.backspace
|
19
|
+
when "left"
|
20
|
+
@valuebuff.left
|
21
|
+
when "right"
|
22
|
+
@valuebuff.right
|
23
|
+
when /[\w\s]/
|
24
|
+
@valuebuff << key
|
25
|
+
else
|
26
|
+
raise ArgumentError, "No handler for key '#{key}' (while focused on this blank)"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def position
|
31
|
+
@valuebuff.position > 0 ? @valuebuff.position : 0
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'base'
|
2
|
+
require_relative 'mixins/value_buffer_delegate'
|
3
|
+
|
4
|
+
class MadClibs
|
5
|
+
module Blanks
|
6
|
+
class String < Base
|
7
|
+
include Mixins::ValueBufferDelegate
|
8
|
+
|
9
|
+
def initialize(s)
|
10
|
+
@start_value = s.to_s
|
11
|
+
@completions = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def render
|
15
|
+
restore_color(value)
|
16
|
+
end
|
17
|
+
|
18
|
+
def valid?
|
19
|
+
!empty?
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'term/ansicolor'
|
2
|
+
|
3
|
+
class MadCLIbs
|
4
|
+
class Line
|
5
|
+
def initialize(*tokens)
|
6
|
+
@tokens = []
|
7
|
+
@blanks = []
|
8
|
+
|
9
|
+
tokens.each { |t| self << t }
|
10
|
+
squash_colors
|
11
|
+
end
|
12
|
+
|
13
|
+
def <<(token)
|
14
|
+
@tokens << token
|
15
|
+
if token_is_blank? token
|
16
|
+
@blanks << token
|
17
|
+
end
|
18
|
+
|
19
|
+
self
|
20
|
+
end
|
21
|
+
|
22
|
+
def values
|
23
|
+
values = @blanks.map &:value
|
24
|
+
values.length <= 1? values.first : values
|
25
|
+
end
|
26
|
+
|
27
|
+
def token_is_blank?(token)
|
28
|
+
token.respond_to?(:value) && token.respond_to?(:value=)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Add bare color tokens to the end (or failing that beginning)
|
32
|
+
# of adjacent strings; fixes spacing when using bare colors
|
33
|
+
def squash_colors
|
34
|
+
types = @tokens.map do |t|
|
35
|
+
if token_is_blank? t
|
36
|
+
:blank
|
37
|
+
elsif Term::ANSIColor.uncolored(t).empty?
|
38
|
+
:color
|
39
|
+
else
|
40
|
+
:string
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
@tokens.size.times do |i|
|
45
|
+
case types[i]
|
46
|
+
when :color
|
47
|
+
if i != 0 and types[i-1] == :string
|
48
|
+
@tokens[i-1] = @tokens[i-1] + @tokens[i]
|
49
|
+
@tokens[i] = nil
|
50
|
+
i += 1
|
51
|
+
elsif i == @tokens.size - 1
|
52
|
+
# its fine by itself.
|
53
|
+
elsif types[i+1] == :string
|
54
|
+
@tokens[i+1] = @tokens[i] + @tokens[i+1]
|
55
|
+
@tokens[i] = nil
|
56
|
+
i += 1
|
57
|
+
end
|
58
|
+
else
|
59
|
+
#nothing
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
@tokens = @tokens.reject { |t| t.nil? }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require_relative 'util/iohelper'
|
2
|
+
require 'stringio'
|
3
|
+
require 'term/ansicolor'
|
4
|
+
|
5
|
+
class MadClibs
|
6
|
+
class Prompter
|
7
|
+
def initialize
|
8
|
+
@io = IOHelper
|
9
|
+
reinit
|
10
|
+
end
|
11
|
+
|
12
|
+
def prompt(line)
|
13
|
+
reinit
|
14
|
+
@tokens = line.instance_variable_get(:@tokens)
|
15
|
+
@blanks = line.instance_variable_get(:@blanks)
|
16
|
+
@current_blank_index = 0
|
17
|
+
|
18
|
+
until done?
|
19
|
+
render
|
20
|
+
place_cursor
|
21
|
+
getc
|
22
|
+
end
|
23
|
+
|
24
|
+
post_render
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def reinit
|
29
|
+
@last_char = nil
|
30
|
+
@tokens = nil
|
31
|
+
@blanks = nil
|
32
|
+
@active_blank = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def done?
|
36
|
+
@last_char == 'return'
|
37
|
+
end
|
38
|
+
|
39
|
+
def render
|
40
|
+
buffer = StringIO.new
|
41
|
+
|
42
|
+
@tokens.each do |token|
|
43
|
+
if token.respond_to? :render
|
44
|
+
buffer.print token.render
|
45
|
+
elsif token.respond_to? :to_s
|
46
|
+
buffer.print token
|
47
|
+
end
|
48
|
+
|
49
|
+
buffer.print " "
|
50
|
+
end
|
51
|
+
|
52
|
+
@io.rerender buffer.string
|
53
|
+
end
|
54
|
+
|
55
|
+
def getc
|
56
|
+
@last_char = @io.read_key
|
57
|
+
case @last_char
|
58
|
+
when "up"
|
59
|
+
previous_blank!
|
60
|
+
when "tab", "down"
|
61
|
+
#if @active_blank.has_completions?
|
62
|
+
#end
|
63
|
+
next_blank!
|
64
|
+
when "return", "linefeed"
|
65
|
+
@last_char = "return"
|
66
|
+
else
|
67
|
+
active_blank.key(@last_char)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def place_cursor
|
72
|
+
print @io.carriage_return
|
73
|
+
|
74
|
+
@tokens.each do |t|
|
75
|
+
if t == active_blank
|
76
|
+
print @io.char_right*t.position
|
77
|
+
break
|
78
|
+
end
|
79
|
+
print @io.char_right*(Term::ANSIColor.uncolored(t).length+1) # + 1 for space
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def post_render
|
84
|
+
@tokens.each do |token|
|
85
|
+
token.post_render if token.respond_to? :post_render
|
86
|
+
end
|
87
|
+
render
|
88
|
+
puts ""
|
89
|
+
end
|
90
|
+
|
91
|
+
def next_blank!
|
92
|
+
@current_blank_index += 1
|
93
|
+
@current_blank_index = 0 if @current_blank_index >= @blanks.length
|
94
|
+
end
|
95
|
+
|
96
|
+
def previous_blank!
|
97
|
+
@current_blank_index -= 1
|
98
|
+
@current_blank_index = @blanks.length - 1 if @current_blank_index < 0
|
99
|
+
end
|
100
|
+
|
101
|
+
def active_blank
|
102
|
+
@blanks[@current_blank_index]
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# Copied initially from github.com/arlimus/inquirer.rb (http://git.io/N6xT)
|
2
|
+
# Copyright Dominik Richter, licensed under Apache v2 (http://git.io/N6xC)
|
3
|
+
#
|
4
|
+
# Additional changes by Donald Guy - see:
|
5
|
+
# https://github.com/donaldguy/madCLIbs/blame/master/lib/mad_clibs/util/iohelper.rb
|
6
|
+
|
7
|
+
require 'io/console'
|
8
|
+
|
9
|
+
module IOHelper
|
10
|
+
extend self
|
11
|
+
|
12
|
+
@rendered = ""
|
13
|
+
|
14
|
+
KEYS = {
|
15
|
+
"\t" => "tab",
|
16
|
+
"\r" => "return",
|
17
|
+
"\n" => "linefeed",
|
18
|
+
# "\e" => "escape",
|
19
|
+
"\e[A" => "up",
|
20
|
+
"\e[B" => "down",
|
21
|
+
"\e[C" => "right",
|
22
|
+
"\e[D" => "left",
|
23
|
+
"\177" => "backspace",
|
24
|
+
# ctrl + c
|
25
|
+
"\003" => "ctrl-c",
|
26
|
+
# ctrl + d
|
27
|
+
"\004" => "ctrl-d"
|
28
|
+
}
|
29
|
+
|
30
|
+
# Read a character the user enters on console. This call is synchronous blocking.
|
31
|
+
# This is taken from: http://www.alecjacobson.com/weblog/?p=75
|
32
|
+
def read_char
|
33
|
+
begin
|
34
|
+
# save previous state of stty
|
35
|
+
old_state = `stty -g`
|
36
|
+
# disable echoing and enable raw (not having to press enter)
|
37
|
+
system "stty raw -echo"
|
38
|
+
c = STDIN.getc.chr
|
39
|
+
# gather next two characters of special keys
|
40
|
+
if(c=="\e")
|
41
|
+
extra_thread = Thread.new{
|
42
|
+
c = c + STDIN.getc.chr
|
43
|
+
c = c + STDIN.getc.chr
|
44
|
+
}
|
45
|
+
# wait just long enough for special keys to get swallowed
|
46
|
+
extra_thread.join(0.00001)
|
47
|
+
# kill thread so not-so-long special keys don't wait on getc
|
48
|
+
extra_thread.kill
|
49
|
+
end
|
50
|
+
rescue => ex
|
51
|
+
puts "#{ex.class}: #{ex.message}"
|
52
|
+
puts ex.backtrace
|
53
|
+
ensure
|
54
|
+
# restore previous state of stty
|
55
|
+
system "stty #{old_state}"
|
56
|
+
end
|
57
|
+
return c
|
58
|
+
end
|
59
|
+
|
60
|
+
# Read a keypress on console. Return the key name (e.g. "space", "a", "B")
|
61
|
+
# Params:
|
62
|
+
# +with_exit_codes+:: +Bool+ whether to throw Interrupts when the user presses
|
63
|
+
# ctrl-c and ctrl-d. (true by default)
|
64
|
+
def read_key with_exit_codes = true, return_char = false
|
65
|
+
char = read_char
|
66
|
+
raise Interrupt if with_exit_codes and ( char == "\003" or char == "\004" )
|
67
|
+
if return_char then char else char_to_raw char end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Get each key the user presses and hand it one by one to the block. Do this
|
71
|
+
# as long as the block returns truthy
|
72
|
+
# Params:
|
73
|
+
# +&block+:: +Proc+ a block that receives a user key and returns truthy or falsy
|
74
|
+
def read_key_while return_char = false, &block
|
75
|
+
STDIN.noecho do
|
76
|
+
# as long as the block doen't return falsy,
|
77
|
+
# read the user input key and sned it to the block
|
78
|
+
while block.( IOHelper.read_key true, return_char )
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Get the console window size
|
84
|
+
# Returns: [width, height]
|
85
|
+
def winsize
|
86
|
+
STDIN.winsize
|
87
|
+
end
|
88
|
+
|
89
|
+
# Render a text to the prompt
|
90
|
+
def render(s)
|
91
|
+
@rendered = s
|
92
|
+
print s
|
93
|
+
end
|
94
|
+
|
95
|
+
# Clear the prompt and render the update
|
96
|
+
def rerender(s)
|
97
|
+
clear
|
98
|
+
render s
|
99
|
+
end
|
100
|
+
|
101
|
+
# clear the console based on the last text rendered
|
102
|
+
def clear
|
103
|
+
# get console window height and width
|
104
|
+
h,w = IOHelper.winsize
|
105
|
+
# determine how many lines to move up
|
106
|
+
n = @rendered.scan(/\n/).length
|
107
|
+
# jump back to the first position and clear the line
|
108
|
+
print carriage_return + ( line_up + clear_line ) * n + clear_line
|
109
|
+
end
|
110
|
+
|
111
|
+
# hides the cursor and ensure the curso be visible at the end
|
112
|
+
def without_cursor
|
113
|
+
# tell the terminal to hide the cursor
|
114
|
+
print `tput civis`
|
115
|
+
begin
|
116
|
+
# run the block
|
117
|
+
yield
|
118
|
+
ensure
|
119
|
+
# tell the terminal to show the cursor
|
120
|
+
print `tput cnorm`
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def char_to_raw char
|
125
|
+
KEYS.fetch char, char
|
126
|
+
end
|
127
|
+
|
128
|
+
def carriage_return; "\r" end
|
129
|
+
def line_up; "\e[A" end
|
130
|
+
def clear_line; "\e[0K" end
|
131
|
+
def char_left; "\e[D" end
|
132
|
+
def char_right; "\e[C" end
|
133
|
+
|
134
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class ValueBuffer
|
2
|
+
attr_reader :position
|
3
|
+
|
4
|
+
def initialize(s="")
|
5
|
+
self.value = s
|
6
|
+
end
|
7
|
+
|
8
|
+
def left
|
9
|
+
@position -= 1 if @position >= 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def right
|
13
|
+
@position += 1 if @position < value.length
|
14
|
+
end
|
15
|
+
|
16
|
+
def backspace
|
17
|
+
@buffer = @buffer[0..-2]
|
18
|
+
left
|
19
|
+
end
|
20
|
+
|
21
|
+
def << c
|
22
|
+
if @position < 0
|
23
|
+
@position = 0
|
24
|
+
end
|
25
|
+
@buffer.insert(@position,c)
|
26
|
+
@position += 1
|
27
|
+
end
|
28
|
+
|
29
|
+
def value=(s)
|
30
|
+
@buffer = s.dup
|
31
|
+
@position = -1
|
32
|
+
end
|
33
|
+
|
34
|
+
def value
|
35
|
+
@buffer
|
36
|
+
end
|
37
|
+
end
|
data/lib/mad_clibs.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
class MadClibs
|
2
|
+
autoload :Line, 'mad_clibs/line'
|
3
|
+
autoload :Blanks, 'mad_clibs/blanks'
|
4
|
+
autoload :Prompter, 'mad_clibs/prompter'
|
5
|
+
|
6
|
+
def initialize()
|
7
|
+
@prompter = Prompter.new
|
8
|
+
end
|
9
|
+
|
10
|
+
def prompt(*args)
|
11
|
+
l = Line.new(*args)
|
12
|
+
@prompter.prompt(l)
|
13
|
+
l.values()
|
14
|
+
end
|
15
|
+
|
16
|
+
def string(val)
|
17
|
+
Blanks::String.new(val)
|
18
|
+
end
|
19
|
+
end
|
data/madCLIbs.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mad_clibs/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "madCLIbs"
|
8
|
+
spec.version = MadClibs::VERSION
|
9
|
+
spec.authors = ["Donald Guy"]
|
10
|
+
spec.email = ["fawkes@mit.edu"]
|
11
|
+
spec.summary = %q{User-friendly fill-in-the-blank CLI forms (think HTML5 with placeholders) }
|
12
|
+
spec.description = <<-EOS
|
13
|
+
Seeks to mimic the familiar UX of HTML5 form elements, with placeholder attributes,
|
14
|
+
in an ANSI-compatible terminal.
|
15
|
+
|
16
|
+
Allows a program to present a line with one or more "blanks" which can be edited and moved between
|
17
|
+
at will (with tab or up & down arrow keys). On new line, returns the values of the blanks (treating
|
18
|
+
placeholders as a default values if no editing occured)
|
19
|
+
EOS
|
20
|
+
spec.homepage = "https://github.com/donaldguy/madCLIbs"
|
21
|
+
spec.license = "MIT"
|
22
|
+
|
23
|
+
spec.files = `git ls-files -z`.split("\x0")
|
24
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
25
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
26
|
+
spec.require_paths = ["lib"]
|
27
|
+
|
28
|
+
spec.add_dependency "term-ansicolor", "~> 1.1"
|
29
|
+
|
30
|
+
spec.add_development_dependency "bundler", "~> 1.6"
|
31
|
+
end
|
metadata
ADDED
@@ -0,0 +1,101 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: madCLIbs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Donald Guy
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-02-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: term-ansicolor
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.6'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.6'
|
41
|
+
description: |2
|
42
|
+
Seeks to mimic the familiar UX of HTML5 form elements, with placeholder attributes,
|
43
|
+
in an ANSI-compatible terminal.
|
44
|
+
|
45
|
+
Allows a program to present a line with one or more "blanks" which can be edited and moved between
|
46
|
+
at will (with tab or up & down arrow keys). On new line, returns the values of the blanks (treating
|
47
|
+
placeholders as a default values if no editing occured)
|
48
|
+
email:
|
49
|
+
- fawkes@mit.edu
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- ".gitignore"
|
55
|
+
- Gemfile
|
56
|
+
- LICENSE.txt
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- examples/basic.rb
|
60
|
+
- examples/colors.rb
|
61
|
+
- examples/scoped.rb
|
62
|
+
- lib/madCLIbs.rb
|
63
|
+
- lib/madCLIbs/import.rb
|
64
|
+
- lib/mad_clibs.rb
|
65
|
+
- lib/mad_clibs/blanks.rb
|
66
|
+
- lib/mad_clibs/blanks/base.rb
|
67
|
+
- lib/mad_clibs/blanks/mixins/color_handling.rb
|
68
|
+
- lib/mad_clibs/blanks/mixins/default_key_handling.rb
|
69
|
+
- lib/mad_clibs/blanks/mixins/value_buffer_delegate.rb
|
70
|
+
- lib/mad_clibs/blanks/string.rb
|
71
|
+
- lib/mad_clibs/line.rb
|
72
|
+
- lib/mad_clibs/prompter.rb
|
73
|
+
- lib/mad_clibs/util/iohelper.rb
|
74
|
+
- lib/mad_clibs/util/value_buffer.rb
|
75
|
+
- lib/mad_clibs/version.rb
|
76
|
+
- madCLIbs.gemspec
|
77
|
+
homepage: https://github.com/donaldguy/madCLIbs
|
78
|
+
licenses:
|
79
|
+
- MIT
|
80
|
+
metadata: {}
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 2.2.2
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: User-friendly fill-in-the-blank CLI forms (think HTML5 with placeholders)
|
101
|
+
test_files: []
|