Skip to content

Commit 6d80b99

Browse files
committed
Use Colorize::ColorRGB, show help and highlighted error when namespace/command not found
1 parent 3dfae6f commit 6d80b99

9 files changed

+109
-52
lines changed

Diff for: spec/interaction_spec.cr

+6-2
Original file line numberDiff line numberDiff line change
@@ -51,17 +51,20 @@ describe Kommando::Interaction::Session do
5151
def write(slice : Bytes) : Nil
5252
# STDOUT.puts ".write(#{String.new(slice).inspect})"
5353
raise "Expected :write, got #{current_action[0]}" if current_action[0] != :write
54+
# raise "Invalid length: #{current_action[1].inspect} <=> #{String.new(slice).inspect}" if current_action[1].size != slice.size
5455

5556
slice.size.times { |i|
56-
raise "Unexpected output: #{String.new(slice).inspect} != #{current_action[1].inspect}" if slice[i] != current_action[1].byte_at(@string_idx)
57+
if slice[i] != current_action[1].byte_at(@string_idx)
58+
raise "Unexpected output: #{String.new(slice).inspect} != #{current_action[1].inspect}"
59+
end
5760
@string_idx += 1
5861
}
5962
advance if current_action[1].size == @string_idx
6063
end
6164
end
6265

6366
def session(io, colorize = false)
64-
Kommando::Interaction::Session.define(io, colorize) do |s|
67+
Kommando::Interaction::Session.define(io, io, colorize) do |s|
6568
with s yield(s)
6669
end
6770
end
@@ -146,6 +149,7 @@ describe Kommando::Interaction::Session do
146149
test "confirm" do
147150
io = CannedIO.build do
148151
write "Want to exit?\n"
152+
write "[y, yes, n, no]\n"
149153
write "> "
150154
read "n\n"
151155
end

Diff for: spec/namespace_spec.cr

+9-9
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,11 @@ describe Kommando::Namespace do
9191
assert run_and_capture(["help"]) == <<-STDOUT
9292
Commands:
9393
94-
\e[94m info \e[0m\e[90mPrints information\e[0m
94+
\e[38;2;90;90;250m info \e[0m\e[38;2;100;100;100mPrints information\e[0m
9595
9696
Namespaces:
9797
98-
\e[94mdb\e[0m
98+
\e[38;2;90;90;250mdb\e[0m
9999
\n
100100
STDOUT
101101
end
@@ -104,24 +104,24 @@ describe Kommando::Namespace do
104104
assert run_and_capture(["db", "help"]) == <<-STDOUT
105105
Commands:
106106
107-
\e[94m create \e[0m\e[90mCreate the database\e[0m
108-
\e[94m migrate \e[0m\e[90mRun pending migrations\e[0m
107+
\e[38;2;90;90;250m create \e[0m\e[38;2;100;100;100mCreate the database\e[0m
108+
\e[38;2;90;90;250m migrate \e[0m\e[38;2;100;100;100mRun pending migrations\e[0m
109109
\n
110110
STDOUT
111111
end
112112

113113
test "command help" do
114114
assert run_and_capture(["help", "info"]) == <<-STDOUT
115-
\e[33minfo\e[0m: \e[90mPrints information\e[0m
115+
\e[38;2;220;220;0minfo\e[0m: \e[38;2;100;100;100mPrints information\e[0m
116116
117-
Usage: \e[33minfo\e[0m \e[94mversion\e[0m \e[90m-option=value\e[0m
117+
Usage: \e[38;2;220;220;0minfo\e[0m \e[38;2;90;90;250mversion\e[0m \e[38;2;100;100;100m-option=value\e[0m
118118
119119
Positional:
120-
\e[94m version \e[0m : \e[35mInt32 \e[0m
120+
\e[38;2;90;90;250m version \e[0m : \e[38;2;205;0;205mInt32 \e[0m
121121
122122
Options:
123-
\e[94m dry \e[0m\e[36m-d\e[0m : \e[35mBool \e[0m \e[90mSimulate migration\e[0m
124-
\e[94m verbose \e[0m\e[36m-v\e[0m : \e[35mBool \e[0m \e[90mMore detailed output\e[0m
123+
\e[38;2;90;90;250m dry \e[0m\e[38;2;0;205;205m-d\e[0m : \e[38;2;205;0;205mBool \e[0m \e[38;2;100;100;100mSimulate migration\e[0m
124+
\e[38;2;90;90;250m verbose \e[0m\e[38;2;0;205;205m-v\e[0m : \e[38;2;205;0;205mBool \e[0m \e[38;2;100;100;100mMore detailed output\e[0m
125125
\n
126126
STDOUT
127127
end

Diff for: src/example.cr

+4-2
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ class Example
55

66
option(:income, Int32, "", default: 0, validate: ->(v : Int32) { v >= 0 && v <= 1_000_000 })
77
option(:ssid, String, "", format: /\A[0-9]{10}\z/)
8-
option(:force, Bool, "Description", default: false)
8+
option(:force, Bool, "Force the change", default: false)
99

1010
arg(:name, String)
1111
arg(:age, Int32, validate: ->(v : Int32) { (13..150).includes?(v) })
@@ -15,7 +15,9 @@ class Example
1515
end
1616

1717
cli = Kommando::Namespace.root do
18-
command Example
18+
namespace("examples") do
19+
command Example
20+
end
1921
end
2022

2123
cli.exec(ARGV)

Diff for: src/kommando.cr

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
require "colorize"
22
require "./kommando/version"
3+
require "./kommando/colors"
34
require "./kommando/errors"
45
require "./kommando/parser"
56
require "./kommando/docker"

Diff for: src/kommando/colors.cr

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
module Kommando
2+
alias RGB = Colorize::ColorRGB
3+
4+
GREEN = RGB.new(0, 220, 0)
5+
RED = RGB.new(220, 0, 0)
6+
YELLOW = RGB.new(220, 220, 0)
7+
WHITE = RGB.new(255, 255, 255)
8+
DARK_GRAY = RGB.new(100, 100, 100)
9+
LIGHT_GRAY = RGB.new(200, 200, 200)
10+
MAGENTA = RGB.new(205, 0, 205)
11+
LIGHT_MAGENTA = RGB.new(220, 0, 220)
12+
CYAN = RGB.new(0, 205, 205)
13+
LIGHT_BLUE = RGB.new(90, 90, 250)
14+
end

Diff for: src/kommando/command.cr

+14-12
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ module Kommando
103103
Tuple.new(
104104
{% for var in @type.instance_vars %}
105105
{% if ann = var.annotation(Kommando::Argument) %}
106-
{ pos: {{i}}, {{**ann.named_args}} },
106+
{ pos: {{i}}, {{ann.named_args.double_splat}} },
107107
{% i += 1 %}
108108
{% end %}
109109
{% end %}
@@ -124,9 +124,9 @@ module Kommando
124124
end
125125

126126
def self.describe(io : IO)
127-
io << command_name.colorize(:yellow)
127+
io << command_name.colorize(Kommando::YELLOW)
128128
io << ": "
129-
io.puts description.colorize(:dark_gray)
129+
io.puts description.colorize(Kommando::DARK_GRAY)
130130
io.puts
131131

132132
io << "Usage: "
@@ -144,37 +144,39 @@ module Kommando
144144
end
145145

146146
def self.describe_usage(io : IO)
147-
io << command_name.colorize(:yellow)
147+
io << command_name.colorize(Kommando::YELLOW)
148148
io << " "
149149

150-
positionals.each { |a| io << a[:name].colorize(:light_blue) }
150+
positionals.join(io, " ") { |a, io|
151+
io << a[:name].colorize(Kommando::LIGHT_BLUE)
152+
}
151153

152154
io << " "
153-
io << "-option=value".colorize(:dark_gray)
155+
io << "-option=value".colorize(Kommando::DARK_GRAY)
154156
end
155157

156158
def self.describe_positionals(io : IO)
157159
positionals.each { |a|
158-
io << (" %-10s" % a[:name]).colorize(:light_blue)
160+
io << (" %-10s" % a[:name]).colorize(Kommando::LIGHT_BLUE)
159161
io << " : "
160-
io << ("%-8s" % a[:type]).colorize(:magenta)
162+
io << ("%-8s" % a[:type]).colorize(Kommando::MAGENTA)
161163
io.puts
162164
}
163165
end
164166

165167
def self.describe_options(io : IO)
166168
options.each { |name, o|
167-
io << (" %-10s " % name).colorize(:light_blue)
169+
io << (" %-10s " % name).colorize(Kommando::LIGHT_BLUE)
168170

169171
shortcut = ""
170172
shortcut = ("-" + o[:short].to_s) if o[:short]
171173

172-
io << "%-2s" % shortcut.colorize(:cyan)
174+
io << "%-2s" % shortcut.colorize(Kommando::CYAN)
173175

174176
io << " : "
175-
io << ("%-8s" % o[:type]).colorize(:magenta)
177+
io << ("%-8s" % o[:type]).colorize(Kommando::MAGENTA)
176178
io << " "
177-
io << o[:desc].colorize(:dark_gray)
179+
io << o[:desc].colorize(Kommando::DARK_GRAY)
178180
io.puts
179181
}
180182
end

Diff for: src/kommando/docker.cr

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@ module Kommando
2828
)
2929

3030
# if status.success?
31-
# print "✓ ".colorize(:green)
31+
# print "✓ ".colorize(GREEN)
3232

3333
# # puts "#{name} [#{Docker.images(stdio.to_s).last}]"
3434
# # puts stdio.to_s.split("\n")[-3]
3535
# else
36-
# print "× ".colorize(:red)
36+
# print "× ".colorize(RED)
3737
# puts "#{name}"
3838

3939
# puts stdio.to_s

Diff for: src/kommando/interaction.cr

+52-21
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,28 @@ require "colorize"
33
module Kommando
44
module Interaction
55
class Session
6-
CONFIRM = %w{Y y Yes yes}
7-
DENY = %w{N n No no}
6+
CONFIRM = %w{y yes}
7+
DENY = %w{n no}
88

9-
@io : IO
10-
delegate gets, to: @io
9+
@outp : IO
10+
@inp : IO
1111

1212
getter? colorize : Bool
1313

14-
def self.define(io : IO, colorize : Bool = true)
15-
s = new(io, colorize)
14+
def self.define(outp : IO = STDOUT, inp : IO = STDIN, colorize : Bool = true)
15+
s = new(outp, inp, colorize)
1616
with s yield(s)
1717
end
1818

19-
def initialize(@io, @colorize = true)
19+
def initialize(@outp, @inp, @colorize = true)
20+
end
21+
22+
private def print_question(q : String)
23+
wl(q, fg: CYAN)
24+
end
25+
26+
private def print_input_marker
27+
w("> ", fg: YELLOW)
2028
end
2129

2230
def read_until(&block : String -> T?) : T forall T
@@ -53,12 +61,12 @@ module Kommando
5361
print_question(text)
5462

5563
options.each_with_index { |(key, desc), i|
56-
w("%2d" % (i + 1), fg: :cyan)
64+
w("%2d" % (i + 1), fg: CYAN)
5765
w(" : ")
58-
w(("%8s" % key), fg: :cyan)
66+
w(("%8s" % key), fg: CYAN)
5967
if desc
6068
w(" : ")
61-
w(desc, fg: :dark_gray)
69+
w(desc, fg: DARK_GRAY)
6270
end
6371
br
6472
}
@@ -81,16 +89,31 @@ module Kommando
8189
end
8290

8391
def confirm(text : String, confirm : Array(String) = CONFIRM, deny : Array(String) = DENY)
84-
ask(text) { |answer|
85-
case answer
92+
options = "[#{(CONFIRM + DENY).join(", ")}]"
93+
94+
wl(text)
95+
wl(options, fg: LIGHT_GRAY)
96+
97+
read_until { |answer|
98+
case answer.downcase
8699
when .in?(CONFIRM) then true
87100
when .in?(DENY) then false
101+
else wl("Invalid input", fg: RED)
88102
end
89103
}
104+
# print_question(text + "[#{(CONFIRM + DENY).join(", ")}]")
105+
106+
# read_until { |a|
107+
# case a
108+
# when .in?(CONFIRM) then true
109+
# when .in?(DENY) then false
110+
# else print_question(text)
111+
# end
112+
# }
90113
end
91114

92115
def read_string_once
93-
gets || ""
116+
@inp.gets || ""
94117
end
95118

96119
def read_once(type : Int32.class | Float32.class)
@@ -102,38 +125,46 @@ module Kommando
102125
end
103126
end
104127

105-
def print_question(q : String)
106-
w(q, "\n", fg: :blue)
128+
def cancel(s : String = "Cancelled")
129+
wl(s, fg: YELLOW)
130+
exit 0
107131
end
108132

109-
def print_input_marker
110-
w("> ", fg: :yellow)
133+
def abort(s : String)
134+
wl(s, fg: RED)
135+
exit 1
111136
end
112137

113138
def br
114139
w("\n")
115140
end
116141

117-
def colorized_io(fg : Symbol? = nil, bg : Symbol? = nil, m : Colorize::Mode? = nil)
142+
def colorized_io(fg : RGB? = nil, bg : RGB? = nil, m : Colorize::Mode? = nil)
118143
if colorize?
119144
c = Colorize.with
120145
c = c.fore(fg) if fg
121146
c = c.mode(m) if m
122147
c = c.back(bg) if bg
123148

124-
c.surround(@io) do |cio|
149+
c.surround(@outp) do |cio|
125150
yield cio
126151
end
127152
else
128-
yield @io
153+
yield @outp
129154
end
130155
end
131156

132-
def w(*strs : String | Int32 | Nil, fg : Symbol? = nil, bg : Symbol? = nil, m : Colorize::Mode? = nil)
157+
def w(*strs : String | Int32 | Nil, fg : RGB? = nil, bg : RGB? = nil, m : Colorize::Mode? = nil)
133158
colorized_io(fg, bg, m) do |cio|
134159
strs.each { |s| cio << s }
135160
end
136161
end
162+
163+
def wl(*strs : String | Int32 | Nil, **options)
164+
# w(*strs + {"\n"}, **options)
165+
w(*strs, **options)
166+
br
167+
end
137168
end
138169
end
139170
end

Diff for: src/kommando/namespace.cr

+7-4
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,10 @@ module Kommando
6666
elsif ns = @namespaces[arg]?
6767
ns.run(args, io)
6868
else
69-
raise "Unrecognized command or namespace: #{arg.inspect}"
69+
io.puts "Unrecognized command or namespace: #{arg.inspect}".colorize(RED)
70+
io.puts
71+
help([] of String, io)
72+
exit 1
7073
end
7174
end
7275

@@ -77,9 +80,9 @@ module Kommando
7780
io.puts
7881

7982
@commands.each do |name, cmd|
80-
io << (" %-16s" % name).colorize(:light_blue)
83+
io << (" %-16s" % name).colorize(LIGHT_BLUE)
8184

82-
io << cmd.description.colorize(:dark_gray)
85+
io << cmd.description.colorize(DARK_GRAY)
8386

8487
io.puts
8588
end
@@ -93,7 +96,7 @@ module Kommando
9396

9497
@namespaces.each do |name, _ns|
9598
io << " "
96-
io << name.colorize(:light_blue)
99+
io << name.colorize(LIGHT_BLUE)
97100
io.puts
98101
end
99102

0 commit comments

Comments
 (0)