shellopts 2.0.21 → 2.0.24
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 +4 -4
- data/lib/shellopts/argument_type.rb +76 -24
- data/lib/shellopts/formatter.rb +12 -7
- data/lib/shellopts/parser.rb +2 -2
- data/lib/shellopts/renderer.rb +19 -20
- data/lib/shellopts/version.rb +1 -1
- data/lib/shellopts.rb +1 -1
- data/shellopts.gemspec +2 -3
- metadata +8 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fff67178855b9164ee7e432d2074040c4c7d8249231e83f27ce1b6a345dbad4c
|
4
|
+
data.tar.gz: 9c9816fda6e7361e310080fd9d150d0c9e7419de929e42a552cfa8dd4c9d7740
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4c6f20dd0afbf624cc671ffc2468977861a3533c5f447acadf3082cdf577dbb9547a96e5ece6cc78fc9f93045dea99581d37f6f7e521e2bcc43261bec07cf653
|
7
|
+
data.tar.gz: 773fde53cd201e58c8d860d19861c5d33c2ce0214d5c5986cc9ce5d91d5b920e33f54398a2b6e9185c7300504ce82169c94e1d92854d68ce76c8cf9050b035f8
|
@@ -18,7 +18,8 @@ module ShellOpts
|
|
18
18
|
# if type is an IntegerArgument
|
19
19
|
def value?(value) true end
|
20
20
|
|
21
|
-
# Convert value to Ruby type
|
21
|
+
# Convert value to Ruby type. This method can also be used to translate
|
22
|
+
# special keywords
|
22
23
|
def convert(value) value end
|
23
24
|
|
24
25
|
# String representation. Equal to #name
|
@@ -60,69 +61,120 @@ module ShellOpts
|
|
60
61
|
attr_reader :kind
|
61
62
|
|
62
63
|
def initialize(kind)
|
63
|
-
constrain kind, :file, :dir, :path, :efile, :edir, :epath, :nfile, :ndir, :npath
|
64
|
+
constrain kind, :file, :dir, :path, :efile, :edir, :epath, :nfile, :ndir, :npath, :ifile, :ofile
|
64
65
|
@kind = kind
|
65
66
|
end
|
66
67
|
|
67
68
|
def match?(name, literal)
|
68
|
-
case
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
69
|
+
# Special-case '-' keyword
|
70
|
+
if literal == '-' && [:ifile, :ofile].include?(kind)
|
71
|
+
true
|
72
|
+
|
73
|
+
# Special-case standard I/O files
|
74
|
+
elsif %w(/dev/stdin /dev/stdout /dev/stderr /dev/null).include?(literal)
|
75
|
+
case kind
|
76
|
+
when :file, :path, :efile, :epath, :nfile, :npath
|
77
|
+
true
|
78
|
+
when :ifile
|
79
|
+
%w(/dev/stdin /dev/null).include? literal or
|
80
|
+
set_message "Can't read #{literal}"
|
81
|
+
when :ofile
|
82
|
+
%w(/dev/stdout /dev/stderr /dev/null).include?(literal) or
|
83
|
+
set_message "Can't write to #{literal}"
|
84
|
+
when :dir, :edir, :ndir
|
85
|
+
set_message "Expected file, got directory #{literal}"
|
86
|
+
else
|
87
|
+
raise ArgumentError, "Unhandled kind: #{kind.inspect}"
|
88
|
+
end
|
89
|
+
|
90
|
+
# All other files or directories
|
80
91
|
else
|
81
|
-
|
92
|
+
case kind # TODO: node, enode, npath - special files: character, block, socket, etc.
|
93
|
+
when :file; match_path(name, literal, kind, :file?, :default)
|
94
|
+
when :dir; match_path(name, literal, kind, :directory?, :default)
|
95
|
+
when :path; match_path(name, literal, kind, :exist?, :default)
|
96
|
+
|
97
|
+
when :efile; match_path(name, literal, kind, :file?, :exist)
|
98
|
+
when :edir; match_path(name, literal, kind, :directory?, :exist)
|
99
|
+
when :epath; match_path(name, literal, kind, :exist?, :exist)
|
100
|
+
|
101
|
+
when :nfile; match_path(name, literal, kind, :file?, :new)
|
102
|
+
when :ndir; match_path(name, literal, kind, :directory?, :new)
|
103
|
+
when :npath; match_path(name, literal, kind, :exist?, :new)
|
104
|
+
|
105
|
+
when :ifile; match_path(name, literal, kind, :readable?, :default)
|
106
|
+
when :ofile; match_path(name, literal, kind, :writable?, :default)
|
107
|
+
else
|
108
|
+
raise InternalError, "Illegal kind: #{kind.inspect}"
|
109
|
+
end
|
82
110
|
end
|
83
111
|
end
|
84
112
|
|
85
113
|
# Note: No checks done, not sure if it is a feature or a bug
|
86
114
|
def value?(value) value.is_a?(String) end
|
87
115
|
|
116
|
+
def convert(value)
|
117
|
+
if value == "-"
|
118
|
+
case kind
|
119
|
+
when :ifile; "/dev/stdin"
|
120
|
+
when :ofile; "/dev/stdout"
|
121
|
+
else
|
122
|
+
value
|
123
|
+
end
|
124
|
+
else
|
125
|
+
value
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
88
129
|
protected
|
89
130
|
def match_path(name, literal, kind, method, mode)
|
90
131
|
subject =
|
91
132
|
case kind
|
92
|
-
when :file, :efile, :nfile; "
|
133
|
+
when :file, :efile, :nfile, :ifile, :ofile; "file"
|
93
134
|
when :dir, :edir, :ndir; "directory"
|
94
135
|
when :path, :epath, :npath; "path"
|
95
136
|
else
|
96
137
|
raise ArgumentError
|
97
138
|
end
|
98
139
|
|
99
|
-
|
140
|
+
# file exists and is the rigth type?
|
141
|
+
if File.send(method, literal)
|
100
142
|
if mode == :new
|
101
143
|
set_message "#{subject.capitalize} already exists in #{name}: #{literal}"
|
102
144
|
elsif kind == :path || kind == :epath
|
103
145
|
if File.file?(literal) || File.directory?(literal)
|
104
146
|
true
|
105
147
|
else
|
106
|
-
set_message "Expected
|
148
|
+
set_message "Expected file or directory as #{name} argument: #{literal}"
|
107
149
|
end
|
150
|
+
elsif (kind == :ifile || kind == :ofile) && File.directory?(literal)
|
151
|
+
set_message "File expected, got directory: #{literal}"
|
108
152
|
else
|
109
153
|
true
|
110
154
|
end
|
111
|
-
|
112
|
-
|
113
|
-
|
155
|
+
|
156
|
+
# file exists but not the right type?
|
157
|
+
elsif File.exist?(literal)
|
158
|
+
if kind == :ifile
|
159
|
+
set_message "Can't read #{literal}"
|
160
|
+
elsif kind == :ofile
|
161
|
+
set_message "Can't write to #{literal}"
|
162
|
+
elsif mode == :new
|
163
|
+
set_message "#{subject.capitalize} already exists - #{literal}"
|
114
164
|
else
|
115
165
|
set_message "Expected #{subject} as #{name} argument: #{literal}"
|
116
166
|
end
|
117
|
-
|
167
|
+
|
168
|
+
# file does not exist
|
169
|
+
else
|
118
170
|
if [:default, :new].include? mode
|
119
|
-
if File.exist?(File.dirname(literal))
|
171
|
+
if File.exist?(File.dirname(literal)) || kind == :ofile
|
120
172
|
true
|
121
173
|
else
|
122
174
|
set_message "Illegal path in #{name}: #{literal}"
|
123
175
|
end
|
124
176
|
else
|
125
|
-
set_message "
|
177
|
+
set_message "Can't find #{literal}"
|
126
178
|
end
|
127
179
|
end
|
128
180
|
end
|
data/lib/shellopts/formatter.rb
CHANGED
@@ -30,8 +30,8 @@ module ShellOpts
|
|
30
30
|
class Command
|
31
31
|
using Ext::Array::Wrap
|
32
32
|
|
33
|
-
def puts_usage(bol: false)
|
34
|
-
width = [Formatter.rest,
|
33
|
+
def puts_usage(bol: false, max_width: Formatter::USAGE_MAX_WIDTH)
|
34
|
+
width = [Formatter.rest, max_width].min
|
35
35
|
if descrs.size == 0
|
36
36
|
print (lead = Formatter.command_prefix || "")
|
37
37
|
indent(lead.size, ' ', bol: bol && lead == "") {
|
@@ -41,7 +41,7 @@ module ShellOpts
|
|
41
41
|
lead = Formatter.command_prefix || ""
|
42
42
|
descrs.each { |descr|
|
43
43
|
print lead
|
44
|
-
puts render(:
|
44
|
+
puts render(:multi, width, args: descr.text.split(' '))
|
45
45
|
}
|
46
46
|
end
|
47
47
|
end
|
@@ -58,7 +58,7 @@ module ShellOpts
|
|
58
58
|
end
|
59
59
|
|
60
60
|
puts "Usage"
|
61
|
-
indent { puts_usage(bol: true) }
|
61
|
+
indent { puts_usage(bol: true, max_width: Formatter::HELP_MAX_WIDTH) }
|
62
62
|
|
63
63
|
if options.any?
|
64
64
|
puts
|
@@ -112,14 +112,13 @@ module ShellOpts
|
|
112
112
|
puts
|
113
113
|
|
114
114
|
puts Ansi.bold "USAGE"
|
115
|
-
indent { puts_usage(bol: true) }
|
115
|
+
indent { puts_usage(bol: true, max_width: Formatter::HELP_MAX_WIDTH) }
|
116
116
|
|
117
117
|
section = {
|
118
118
|
Paragraph => "DESCRIPTION",
|
119
119
|
OptionGroup => "OPTION",
|
120
120
|
Command => "COMMAND"
|
121
121
|
}
|
122
|
-
|
123
122
|
seen_sections = {}
|
124
123
|
newline = false # True if a newline should be printed before child
|
125
124
|
indent {
|
@@ -173,7 +172,10 @@ module ShellOpts
|
|
173
172
|
end
|
174
173
|
|
175
174
|
module WrappedNode
|
176
|
-
def puts_descr
|
175
|
+
def puts_descr
|
176
|
+
width = [Formatter.rest, Formatter::HELP_MAX_WIDTH].min
|
177
|
+
puts lines(width)
|
178
|
+
end
|
177
179
|
end
|
178
180
|
|
179
181
|
class Code
|
@@ -217,6 +219,9 @@ module ShellOpts
|
|
217
219
|
# Indent to use in help output
|
218
220
|
HELP_INDENT = 4
|
219
221
|
|
222
|
+
# Max. width of help text (not including indent)
|
223
|
+
HELP_MAX_WIDTH = 85
|
224
|
+
|
220
225
|
# Command prefix when subject is a sub-command
|
221
226
|
def self.command_prefix() @command_prefix end
|
222
227
|
|
data/lib/shellopts/parser.rb
CHANGED
@@ -83,8 +83,8 @@ module ShellOpts
|
|
83
83
|
when "$"
|
84
84
|
@argument_name ||= "NUM"
|
85
85
|
@argument_type = FloatArgument.new
|
86
|
-
when "FILE", "DIR", "PATH", "EFILE", "EDIR", "EPATH", "NFILE", "NDIR", "NPATH"
|
87
|
-
@argument_name ||= arg.sub(/^(?:E|N)/, "")
|
86
|
+
when "FILE", "DIR", "PATH", "EFILE", "EDIR", "EPATH", "NFILE", "NDIR", "NPATH", "IFILE", "OFILE"
|
87
|
+
@argument_name ||= arg.sub(/^(?:E|N|I|O)/, "")
|
88
88
|
@argument_type = FileArgument.new(arg.downcase.to_sym)
|
89
89
|
when /,/
|
90
90
|
@argument_name ||= arg
|
data/lib/shellopts/renderer.rb
CHANGED
@@ -15,9 +15,9 @@ require 'terminfo'
|
|
15
15
|
#
|
16
16
|
# Command rendering
|
17
17
|
# cmd --all --beta [cmd1|cmd2] ARG1 ARG2 # Single-line formats (:single)
|
18
|
-
# cmd --all --beta [cmd1|cmd2] ARGS...
|
18
|
+
# cmd --all --beta [cmd1|cmd2] ARGS... # Not used
|
19
19
|
# cmd -a -b [cmd1|cmd2] ARG1 ARG2
|
20
|
-
# cmd -a -b [cmd1|cmd2] ARGS...
|
20
|
+
# cmd -a -b [cmd1|cmd2] ARGS... # Not used
|
21
21
|
#
|
22
22
|
# cmd -a -b [cmd1|cmd2] ARG1 ARG2 # One line for each argument description (:enum)
|
23
23
|
# cmd -a -b [cmd1|cmd2] ARG3 ARG4 # (used in the USAGE section)
|
@@ -108,7 +108,7 @@ module ShellOpts
|
|
108
108
|
def get_args(args: nil)
|
109
109
|
case descrs.size
|
110
110
|
when 0; []
|
111
|
-
when 1;
|
111
|
+
when 1; descrs.first.text.split(' ')
|
112
112
|
else [DESCRS_ABBR]
|
113
113
|
end
|
114
114
|
end
|
@@ -120,6 +120,7 @@ module ShellOpts
|
|
120
120
|
end
|
121
121
|
|
122
122
|
# Force one line. Compact options, commands, arguments if needed
|
123
|
+
#
|
123
124
|
def render_single(width, args: nil)
|
124
125
|
long_options = options.map { |option| option.render(:long) }
|
125
126
|
short_options = options.map { |option| option.render(:short) }
|
@@ -164,32 +165,30 @@ module ShellOpts
|
|
164
165
|
def render_multi(width, args: nil)
|
165
166
|
long_options = options.map { |option| option.render(:long) }
|
166
167
|
short_options = options.map { |option| option.render(:short) }
|
167
|
-
|
168
|
-
|
168
|
+
compact_options = options.empty? ? [] : [OPTIONS_ABBR]
|
169
|
+
|
170
|
+
# Only compact commands if they can't fit on one line
|
171
|
+
if commands.empty?
|
172
|
+
use_commands = []
|
173
|
+
else
|
174
|
+
short_command = "[#{commands.map(&:name).join("|")}]"
|
175
|
+
use_commands = [short_command.size > width ? COMMANDS_ABBR : short_command]
|
176
|
+
end
|
169
177
|
|
170
178
|
args ||= get_args
|
171
179
|
|
172
180
|
# On one line
|
173
|
-
words = [name] + long_options +
|
181
|
+
words = [name] + long_options + use_commands + args
|
174
182
|
return [words.join(" ")] if pass?(words, width)
|
175
|
-
words = [name] + short_options +
|
183
|
+
words = [name] + short_options + use_commands + args
|
184
|
+
return [words.join(" ")] if pass?(words, width)
|
185
|
+
words = [name] + compact_options + use_commands + args
|
176
186
|
return [words.join(" ")] if pass?(words, width)
|
177
187
|
|
178
188
|
# On multiple lines
|
179
189
|
lead = name + " "
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
begin
|
184
|
-
words = short_commands + args
|
185
|
-
break if pass?(words, width)
|
186
|
-
words = compact_commands + args
|
187
|
-
break if pass?(words, width)
|
188
|
-
words = compact_commands + [DESCRS_ABBR]
|
189
|
-
end while false
|
190
|
-
|
191
|
-
cmdargs = words.empty? ? [] : [words.join(" ")]
|
192
|
-
options + indent_lines(lead.size, cmdargs)
|
190
|
+
words = (long_options + use_commands + args).wrap(width - lead.size)
|
191
|
+
lines = [lead + words[0]] + indent_lines(lead.size, words[1..-1])
|
193
192
|
end
|
194
193
|
|
195
194
|
protected
|
data/lib/shellopts/version.rb
CHANGED
data/lib/shellopts.rb
CHANGED
data/shellopts.gemspec
CHANGED
@@ -28,8 +28,7 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_dependency "ruby-terminfo"
|
29
29
|
spec.add_dependency "indented_io"
|
30
30
|
|
31
|
-
spec.add_development_dependency "
|
32
|
-
spec.add_development_dependency "
|
33
|
-
spec.add_development_dependency "rspec", "~> 3.0"
|
31
|
+
spec.add_development_dependency "rake"
|
32
|
+
spec.add_development_dependency "rspec"
|
34
33
|
spec.add_development_dependency "simplecov"
|
35
34
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shellopts
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.0.
|
4
|
+
version: 2.0.24
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Claus Rasmussen
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: forward_to
|
@@ -66,48 +66,34 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: bundler
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - "~>"
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 2.2.10
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - "~>"
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 2.2.10
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
70
|
name: rake
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
86
72
|
requirements:
|
87
73
|
- - ">="
|
88
74
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
75
|
+
version: '0'
|
90
76
|
type: :development
|
91
77
|
prerelease: false
|
92
78
|
version_requirements: !ruby/object:Gem::Requirement
|
93
79
|
requirements:
|
94
80
|
- - ">="
|
95
81
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
82
|
+
version: '0'
|
97
83
|
- !ruby/object:Gem::Dependency
|
98
84
|
name: rspec
|
99
85
|
requirement: !ruby/object:Gem::Requirement
|
100
86
|
requirements:
|
101
|
-
- - "
|
87
|
+
- - ">="
|
102
88
|
- !ruby/object:Gem::Version
|
103
|
-
version: '
|
89
|
+
version: '0'
|
104
90
|
type: :development
|
105
91
|
prerelease: false
|
106
92
|
version_requirements: !ruby/object:Gem::Requirement
|
107
93
|
requirements:
|
108
|
-
- - "
|
94
|
+
- - ">="
|
109
95
|
- !ruby/object:Gem::Version
|
110
|
-
version: '
|
96
|
+
version: '0'
|
111
97
|
- !ruby/object:Gem::Dependency
|
112
98
|
name: simplecov
|
113
99
|
requirement: !ruby/object:Gem::Requirement
|