clamp 1.5.0 → 1.5.1
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/CHANGES.md +4 -0
- data/lib/clamp/completion/bash_generator.rb +101 -35
- data/lib/clamp/completion/fish_generator.rb +111 -10
- data/lib/clamp/completion/zsh_generator.rb +22 -25
- data/lib/clamp/completion.rb +24 -0
- data/lib/clamp/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d0987845ae98be0a62e6501a780e42f4e73826df831d5d3809e17c8f8d335db2
|
|
4
|
+
data.tar.gz: 07665f169706158283c5e3d5fa9e231d668fa7105b8f4726e46f801679173071
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1a2b310ce3f8a1e2cd0bb100abf9a1fec7b8dc36ad639f4b2007824136dd77409d460a2305d36fba0c11999c5e8620406fef5326ab749e31505809631671ee0e
|
|
7
|
+
data.tar.gz: 3fbc558438ae5dd809e60562a9b799c034a3bbbf620eeb77a7cec2ca0c8eb4dd35a86330214a94bde6c22a449974527dc480c9d4278774af0dacb0212a6556fe
|
data/CHANGES.md
CHANGED
|
@@ -19,22 +19,25 @@ module Clamp
|
|
|
19
19
|
"",
|
|
20
20
|
takes_value_function,
|
|
21
21
|
"",
|
|
22
|
-
|
|
22
|
+
param_count_function,
|
|
23
23
|
"",
|
|
24
|
-
|
|
24
|
+
canonical_function,
|
|
25
|
+
"",
|
|
26
|
+
main_function,
|
|
27
|
+
"",
|
|
28
|
+
"complete -F #{completion_function} #{@executable_name}"
|
|
25
29
|
].push("").join("\n")
|
|
26
30
|
end
|
|
27
31
|
|
|
28
32
|
private
|
|
29
33
|
|
|
30
|
-
def
|
|
31
|
-
@executable_name
|
|
34
|
+
def completion_function
|
|
35
|
+
"_clamp_complete_#{Completion.encode_name(@executable_name)}"
|
|
32
36
|
end
|
|
33
37
|
|
|
34
|
-
def
|
|
35
|
-
fn = function_name
|
|
38
|
+
def main_function
|
|
36
39
|
[
|
|
37
|
-
"
|
|
40
|
+
"#{completion_function}() {",
|
|
38
41
|
" local cur prev",
|
|
39
42
|
" if type _init_completion &>/dev/null; then",
|
|
40
43
|
" _init_completion",
|
|
@@ -43,13 +46,19 @@ module Clamp
|
|
|
43
46
|
' prev="${COMP_WORDS[COMP_CWORD-1]}"',
|
|
44
47
|
" fi",
|
|
45
48
|
"",
|
|
46
|
-
" local subcmd",
|
|
47
|
-
"
|
|
48
|
-
|
|
49
|
+
" local subcmd_info subcmd params_remaining",
|
|
50
|
+
" subcmd_info=$(#{completion_function}_find_subcmd)",
|
|
51
|
+
' subcmd="${subcmd_info%% *}"',
|
|
52
|
+
' params_remaining="${subcmd_info##* }"',
|
|
53
|
+
" if #{completion_function}_takes_value \"$prev\" \"$subcmd\"; then",
|
|
49
54
|
" return",
|
|
50
55
|
" fi",
|
|
51
56
|
"",
|
|
52
|
-
|
|
57
|
+
options_case("$subcmd"),
|
|
58
|
+
"",
|
|
59
|
+
' if [ "$params_remaining" -eq 0 ]; then',
|
|
60
|
+
subcommands_case("$subcmd"),
|
|
61
|
+
" fi",
|
|
53
62
|
"}",
|
|
54
63
|
"",
|
|
55
64
|
find_subcmd_function
|
|
@@ -57,54 +66,111 @@ module Clamp
|
|
|
57
66
|
end
|
|
58
67
|
|
|
59
68
|
def find_subcmd_function
|
|
60
|
-
fn = function_name
|
|
61
|
-
subcmds = Completion.collect_subcommand_names(@command_class).join("|")
|
|
62
69
|
[
|
|
63
|
-
"
|
|
64
|
-
" local i=1 word subcmd",
|
|
70
|
+
"#{completion_function}_find_subcmd() {",
|
|
71
|
+
" local i=1 word subcmd skip",
|
|
72
|
+
" skip=$(#{completion_function}_param_count \"\")",
|
|
65
73
|
' while [ "$i" -lt "$COMP_CWORD" ]; do',
|
|
66
74
|
' word="${COMP_WORDS[$i]}"',
|
|
67
75
|
' case "$word" in',
|
|
68
76
|
" -*)",
|
|
69
|
-
" if
|
|
77
|
+
" if #{completion_function}_takes_value \"$word\" \"$subcmd\"; then",
|
|
70
78
|
" ((i++))",
|
|
71
79
|
" fi",
|
|
72
80
|
" ;;",
|
|
73
81
|
" *)",
|
|
74
|
-
|
|
75
|
-
" #{subcmds})",
|
|
76
|
-
' if [ -z "$subcmd" ]; then',
|
|
77
|
-
' subcmd="$word"',
|
|
78
|
-
" else",
|
|
79
|
-
' subcmd="${subcmd}::${word}"',
|
|
80
|
-
" fi",
|
|
81
|
-
" ;;",
|
|
82
|
-
" esac",
|
|
82
|
+
find_subcmd_match_word,
|
|
83
83
|
" ;;",
|
|
84
84
|
" esac",
|
|
85
85
|
" ((i++))",
|
|
86
86
|
" done",
|
|
87
|
-
' echo "$subcmd"',
|
|
87
|
+
' echo "$subcmd $skip"',
|
|
88
88
|
"}"
|
|
89
89
|
].join("\n")
|
|
90
90
|
end
|
|
91
91
|
|
|
92
|
-
def
|
|
92
|
+
def find_subcmd_match_word
|
|
93
|
+
subcmds = Completion.collect_subcommand_names(@command_class).join("|")
|
|
94
|
+
[
|
|
95
|
+
' if [ "$skip" -gt 0 ]; then',
|
|
96
|
+
" ((skip--))",
|
|
97
|
+
" else",
|
|
98
|
+
' case "$word" in',
|
|
99
|
+
" #{subcmds})",
|
|
100
|
+
" local canonical=$(#{completion_function}_canonical \"$word\")",
|
|
101
|
+
' if [ -z "$subcmd" ]; then',
|
|
102
|
+
' subcmd="$canonical"',
|
|
103
|
+
" else",
|
|
104
|
+
' subcmd="${subcmd}::${canonical}"',
|
|
105
|
+
" fi",
|
|
106
|
+
" skip=$(#{completion_function}_param_count \"$subcmd\")",
|
|
107
|
+
" ;;",
|
|
108
|
+
" esac",
|
|
109
|
+
" fi"
|
|
110
|
+
].join("\n")
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def options_case(var)
|
|
114
|
+
build_case(var, " ", "COMPREPLY=") do |cmd, _has_children|
|
|
115
|
+
Completion.visible_options(cmd).flat_map { |o| Completion.expanded_switches(o) }
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def subcommands_case(var)
|
|
120
|
+
build_case(var, " ", "COMPREPLY+=") do |cmd, has_children|
|
|
121
|
+
cmd.recognised_subcommands.flat_map(&:names) if has_children
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def build_case(var, indent, assign)
|
|
93
126
|
entries = {}
|
|
94
127
|
Completion.walk_command_tree(@command_class) do |cmd, path, has_children|
|
|
128
|
+
words = yield(cmd, has_children)
|
|
129
|
+
next if words.nil? || words.empty?
|
|
130
|
+
|
|
95
131
|
path_str = path.map { |sub| sub.names.first }.join("::")
|
|
96
|
-
words = Completion.visible_options(cmd).flat_map { |o| Completion.expanded_switches(o) }
|
|
97
|
-
cmd.recognised_subcommands.each { |sub| words.concat(sub.names) } if has_children
|
|
98
132
|
entries[path_str] = words.join(" ")
|
|
99
133
|
end
|
|
100
|
-
lines = ["
|
|
134
|
+
lines = ["#{indent}case \"#{var}\" in"]
|
|
101
135
|
entries.each do |path, words|
|
|
102
136
|
pattern = path.empty? ? '""' : "\"#{path}\""
|
|
103
|
-
lines << "
|
|
104
|
-
lines << "
|
|
105
|
-
lines << "
|
|
137
|
+
lines << "#{indent} #{pattern})"
|
|
138
|
+
lines << "#{indent} #{assign}($(compgen -W \"#{words}\" -- \"$cur\"))"
|
|
139
|
+
lines << "#{indent} ;;"
|
|
140
|
+
end
|
|
141
|
+
lines << "#{indent}esac"
|
|
142
|
+
lines.join("\n")
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def param_count_function
|
|
146
|
+
entries = {}
|
|
147
|
+
Completion.walk_command_tree(@command_class) do |cmd, path, has_children|
|
|
148
|
+
next unless has_children
|
|
149
|
+
|
|
150
|
+
entries[path.map { |s| s.names.first }.join("::")] = Completion.required_parameter_count(cmd)
|
|
151
|
+
end
|
|
152
|
+
build_lookup_function("param_count", entries, "echo", default: "echo 0")
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def canonical_function
|
|
156
|
+
aliases = {}
|
|
157
|
+
Completion.walk_command_tree(@command_class) do |cmd, _path, has_children|
|
|
158
|
+
next unless has_children
|
|
159
|
+
|
|
160
|
+
cmd.recognised_subcommands.each do |sub|
|
|
161
|
+
sub.names.drop(1).each { |name| aliases[name] = sub.names.first }
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
build_lookup_function("canonical", aliases, "echo", default: 'echo "$1"')
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def build_lookup_function(suffix, entries, verb, default:)
|
|
168
|
+
lines = ["#{completion_function}_#{suffix}() {", ' case "$1" in']
|
|
169
|
+
entries.each do |key, value|
|
|
170
|
+
pattern = key.empty? ? '""' : key
|
|
171
|
+
lines << " #{pattern}) #{verb} #{value.is_a?(String) ? "\"#{value}\"" : value} ;;"
|
|
106
172
|
end
|
|
107
|
-
lines
|
|
173
|
+
lines.push(" *) #{default} ;;", " esac", "}")
|
|
108
174
|
lines.join("\n")
|
|
109
175
|
end
|
|
110
176
|
|
|
@@ -116,7 +182,7 @@ module Clamp
|
|
|
116
182
|
.flat_map { |o| Completion.expanded_switches(o) }
|
|
117
183
|
end
|
|
118
184
|
lines = [
|
|
119
|
-
"
|
|
185
|
+
"#{completion_function}_takes_value() {",
|
|
120
186
|
' local option="$1"',
|
|
121
187
|
' local subcmd="$2"',
|
|
122
188
|
' case "$subcmd" in'
|
|
@@ -16,6 +16,7 @@ module Clamp
|
|
|
16
16
|
lines << "# Fish completions for #{@executable_name}"
|
|
17
17
|
lines << "# Generated by Clamp"
|
|
18
18
|
lines << ""
|
|
19
|
+
helpers = [subcmd_args_function]
|
|
19
20
|
Completion.walk_command_tree(@command_class) do |cmd, path, has_children|
|
|
20
21
|
child_names = has_children ? cmd.recognised_subcommands.flat_map(&:names) : []
|
|
21
22
|
condition = condition_for(path, child_names)
|
|
@@ -24,29 +25,77 @@ module Clamp
|
|
|
24
25
|
end
|
|
25
26
|
next unless has_children
|
|
26
27
|
|
|
27
|
-
cmd
|
|
28
|
-
|
|
29
|
-
lines << "complete -c #{@executable_name} -f -n '#{condition}' -a #{name} " \
|
|
30
|
-
"-d '#{escape(sub.description)}'"
|
|
31
|
-
end
|
|
32
|
-
end
|
|
28
|
+
subcmd_condition = subcommand_condition(cmd, path, condition, helpers)
|
|
29
|
+
generate_subcommand_completions(lines, cmd, subcmd_condition)
|
|
33
30
|
lines << ""
|
|
34
31
|
end
|
|
35
|
-
"#{lines.join("\n")}\n"
|
|
32
|
+
"#{helpers.join("\n\n")}\n\n#{lines.join("\n")}\n"
|
|
36
33
|
end
|
|
37
34
|
|
|
38
35
|
private
|
|
39
36
|
|
|
37
|
+
def subcommand_condition(cmd, path, condition, helpers)
|
|
38
|
+
param_count = Completion.required_parameter_count(cmd)
|
|
39
|
+
return condition unless param_count.positive?
|
|
40
|
+
|
|
41
|
+
helper = ParamsSatisfiedHelper.new(@executable_name, path, param_count)
|
|
42
|
+
helpers << helper.to_s
|
|
43
|
+
"#{condition}; and #{helper.function_name}"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def generate_subcommand_completions(lines, cmd, condition)
|
|
47
|
+
cmd.recognised_subcommands.each do |sub|
|
|
48
|
+
sub.names.each do |name|
|
|
49
|
+
lines << "complete -c #{@executable_name} -f -n '#{condition}' -a #{name} " \
|
|
50
|
+
"-d '#{escape(sub.description)}'"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
40
55
|
def condition_for(path, child_names)
|
|
41
56
|
if path.empty?
|
|
42
|
-
"
|
|
57
|
+
"not #{completion_function}_subcmd_args >/dev/null"
|
|
43
58
|
else
|
|
44
|
-
parts = path.map { |sub| "
|
|
45
|
-
parts << "not
|
|
59
|
+
parts = path.map { |sub| "#{completion_function}_seen_subcommand_from #{sub.names.join(' ')}" }
|
|
60
|
+
parts << "not #{completion_function}_seen_subcommand_from #{child_names.join(' ')}" if child_names.any?
|
|
46
61
|
parts.join("; and ")
|
|
47
62
|
end
|
|
48
63
|
end
|
|
49
64
|
|
|
65
|
+
def completion_function
|
|
66
|
+
"_clamp_complete_#{@executable_name}"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def subcmd_args_function
|
|
70
|
+
optspecs = argparse_optspecs(@command_class)
|
|
71
|
+
lines = [
|
|
72
|
+
"function #{completion_function}_subcmd_args",
|
|
73
|
+
" set -l tokens (commandline -opc)",
|
|
74
|
+
" set -e tokens[1]",
|
|
75
|
+
" argparse -si #{optspecs.map { |s| "'#{s}'" }.join(' ')} -- $tokens 2>/dev/null",
|
|
76
|
+
" or return 1",
|
|
77
|
+
" test (count $argv) -gt 0",
|
|
78
|
+
" and printf '%s\\n' $argv",
|
|
79
|
+
"end",
|
|
80
|
+
"",
|
|
81
|
+
"function #{completion_function}_seen_subcommand_from",
|
|
82
|
+
" for p in (#{completion_function}_subcmd_args)",
|
|
83
|
+
" if contains -- $p $argv",
|
|
84
|
+
" return 0",
|
|
85
|
+
" end",
|
|
86
|
+
" end",
|
|
87
|
+
" return 1",
|
|
88
|
+
"end"
|
|
89
|
+
]
|
|
90
|
+
lines.join("\n")
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def argparse_optspecs(command_class)
|
|
94
|
+
command_class.recognised_options.flat_map do |option|
|
|
95
|
+
Completion.argparse_specs_for(option)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
50
99
|
def option_completion(option, condition)
|
|
51
100
|
parts = ["complete -c #{@executable_name} -f"]
|
|
52
101
|
parts << "-n '#{condition}'"
|
|
@@ -71,5 +120,57 @@ module Clamp
|
|
|
71
120
|
|
|
72
121
|
end
|
|
73
122
|
|
|
123
|
+
# Generates a fish function that checks whether required parameters
|
|
124
|
+
# have been provided before offering subcommand completions.
|
|
125
|
+
class ParamsSatisfiedHelper
|
|
126
|
+
|
|
127
|
+
def initialize(executable_name, path, required_count)
|
|
128
|
+
@executable_name = executable_name
|
|
129
|
+
@path = path
|
|
130
|
+
@required_count = required_count
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def function_name
|
|
134
|
+
parts = [@executable_name]
|
|
135
|
+
@path.each { |sub| parts << sub.names.first }
|
|
136
|
+
"_clamp_complete_#{parts.join('_')}_params_satisfied"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def to_s
|
|
140
|
+
lines = ["function #{function_name}", " set -l tokens (commandline -opc)", " set -l positional 0"]
|
|
141
|
+
if @path.empty?
|
|
142
|
+
count_positional_root(lines)
|
|
143
|
+
else
|
|
144
|
+
count_positional_nested(lines)
|
|
145
|
+
end
|
|
146
|
+
lines.push(" test $positional -ge #{@required_count}", "end")
|
|
147
|
+
lines.join("\n")
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
private
|
|
151
|
+
|
|
152
|
+
def count_positional_root(lines)
|
|
153
|
+
lines.push(" for token in $tokens[2..]",
|
|
154
|
+
" if not string match -q -- '-*' $token",
|
|
155
|
+
" set positional (math $positional + 1)",
|
|
156
|
+
" end", " end")
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def count_positional_nested(lines)
|
|
160
|
+
lines.push(" set -l depth 0", " for token in $tokens[2..]", " switch $depth")
|
|
161
|
+
@path.each_with_index do |sub, i|
|
|
162
|
+
all_names = sub.names.join(" ")
|
|
163
|
+
lines.push(" case #{i}",
|
|
164
|
+
" if contains -- $token #{all_names}",
|
|
165
|
+
" set depth #{i + 1}", " end")
|
|
166
|
+
end
|
|
167
|
+
lines.push(" case #{@path.length}",
|
|
168
|
+
" if not string match -q -- '-*' $token",
|
|
169
|
+
" set positional (math $positional + 1)",
|
|
170
|
+
" end", " end", " end")
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
end
|
|
174
|
+
|
|
74
175
|
end
|
|
75
176
|
end
|
|
@@ -14,27 +14,27 @@ module Clamp
|
|
|
14
14
|
|
|
15
15
|
def generate
|
|
16
16
|
lines = ["#compdef #{@executable_name}", ""]
|
|
17
|
-
generate_functions(lines, @command_class, [
|
|
18
|
-
lines <<
|
|
17
|
+
generate_functions(lines, @command_class, [completion_function], Set.new)
|
|
18
|
+
lines << completion_function
|
|
19
19
|
lines.push("").join("\n")
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
private
|
|
23
23
|
|
|
24
|
-
def
|
|
25
|
-
@executable_name
|
|
24
|
+
def completion_function
|
|
25
|
+
"_clamp_complete_#{Completion.encode_name(@executable_name)}"
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def generate_functions(lines, command_class, path, visited)
|
|
29
29
|
has_children = command_class.has_subcommands? && !visited.include?(command_class)
|
|
30
30
|
visited |= [command_class]
|
|
31
|
-
func_name =
|
|
31
|
+
func_name = path.join("_")
|
|
32
32
|
|
|
33
33
|
if has_children
|
|
34
34
|
generate_subcommand_node(lines, command_class, path, func_name, visited)
|
|
35
35
|
else
|
|
36
36
|
lines << "#{func_name}() {"
|
|
37
|
-
specs = Completion.visible_options(command_class).
|
|
37
|
+
specs = Completion.visible_options(command_class).flat_map { |o| option_specs(o) }
|
|
38
38
|
generate_arguments_call(lines, specs) if specs.any?
|
|
39
39
|
lines << "}"
|
|
40
40
|
lines << ""
|
|
@@ -46,7 +46,7 @@ module Clamp
|
|
|
46
46
|
lines << " local context state state_descr line"
|
|
47
47
|
lines << " typeset -A opt_args"
|
|
48
48
|
lines << ""
|
|
49
|
-
specs = Completion.visible_options(command_class).
|
|
49
|
+
specs = Completion.visible_options(command_class).flat_map { |o| option_specs(o) }
|
|
50
50
|
specs << "'1:command:->commands'"
|
|
51
51
|
specs << "'*::args:->args'"
|
|
52
52
|
lines << " _arguments -C \\"
|
|
@@ -56,7 +56,7 @@ module Clamp
|
|
|
56
56
|
lines << "}"
|
|
57
57
|
lines << ""
|
|
58
58
|
command_class.recognised_subcommands.each do |sub|
|
|
59
|
-
generate_functions(lines, sub.subcommand_class, path + [
|
|
59
|
+
generate_functions(lines, sub.subcommand_class, path + [Completion.encode_name(sub.names.first)], visited)
|
|
60
60
|
end
|
|
61
61
|
end
|
|
62
62
|
|
|
@@ -88,7 +88,7 @@ module Clamp
|
|
|
88
88
|
lines << " args)"
|
|
89
89
|
lines << " case $line[1] in"
|
|
90
90
|
command_class.recognised_subcommands.each do |sub|
|
|
91
|
-
sub_fn =
|
|
91
|
+
sub_fn = (path + [Completion.encode_name(sub.names.first)]).join("_")
|
|
92
92
|
pattern = sub.names.join("|")
|
|
93
93
|
lines << " #{pattern}) #{sub_fn} ;;"
|
|
94
94
|
end
|
|
@@ -97,23 +97,20 @@ module Clamp
|
|
|
97
97
|
lines << " esac"
|
|
98
98
|
end
|
|
99
99
|
|
|
100
|
-
def
|
|
100
|
+
def option_specs(option)
|
|
101
101
|
expanded = Completion.expanded_switches(option)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
exclusion = expanded.length > 1 ? "(#{expanded.join(' ')})" : ""
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
def sanitize(name)
|
|
116
|
-
name.gsub(/[^a-zA-Z0-9_]/, "_")
|
|
102
|
+
suffix = "[#{escape(option.description)}]"
|
|
103
|
+
suffix += ":#{option.type.to_s.downcase}:" unless option.flag?
|
|
104
|
+
exclusion = expanded.length > 1 ? "'(#{expanded.join(' ')})'" : ""
|
|
105
|
+
short = expanded.find { |s| s.match?(/^-[^-]/) }
|
|
106
|
+
longs = expanded.grep(/^--/)
|
|
107
|
+
|
|
108
|
+
if short && longs.length == 1
|
|
109
|
+
# Braces outside quotes for zsh brace expansion
|
|
110
|
+
["#{exclusion}{#{short},#{longs.first}}'#{suffix}'"]
|
|
111
|
+
else
|
|
112
|
+
expanded.map { |sw| "#{exclusion}'#{sw}#{suffix}'" }
|
|
113
|
+
end
|
|
117
114
|
end
|
|
118
115
|
|
|
119
116
|
def escape(str)
|
data/lib/clamp/completion.rb
CHANGED
|
@@ -33,6 +33,12 @@ module Clamp
|
|
|
33
33
|
|
|
34
34
|
module_function
|
|
35
35
|
|
|
36
|
+
# Encode a name for use as a shell function identifier.
|
|
37
|
+
# Special characters are replaced with _XX hex codes.
|
|
38
|
+
def encode_name(name)
|
|
39
|
+
name.gsub(/[^a-zA-Z0-9_]/) { |c| format("_%02x", c.ord) }
|
|
40
|
+
end
|
|
41
|
+
|
|
36
42
|
def generate(command_class, shell, executable_name)
|
|
37
43
|
generator_class = GENERATORS.fetch(shell) do
|
|
38
44
|
raise ArgumentError, "unsupported shell: #{shell.inspect}"
|
|
@@ -71,6 +77,24 @@ module Clamp
|
|
|
71
77
|
end
|
|
72
78
|
end
|
|
73
79
|
|
|
80
|
+
# Count required, non-multivalued parameters for a command.
|
|
81
|
+
def required_parameter_count(command_class)
|
|
82
|
+
command_class.parameters.count { |p| p.required? && !p.multivalued? }
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Return fish argparse optspecs for an option.
|
|
86
|
+
def argparse_specs_for(option)
|
|
87
|
+
switches = expanded_switches(option)
|
|
88
|
+
suffix = option.flag? ? "" : "="
|
|
89
|
+
short = switches.find { |s| s.match?(/^-[^-]$/) }
|
|
90
|
+
longs = switches.select { |s| s.start_with?("--") }
|
|
91
|
+
if short && longs.length == 1
|
|
92
|
+
["#{short.delete_prefix('-')}/#{longs.first.delete_prefix('--')}#{suffix}"]
|
|
93
|
+
else
|
|
94
|
+
longs.map { |l| "#{l.delete_prefix('--')}#{suffix}" }
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
74
98
|
# Collect all subcommand names across the command tree.
|
|
75
99
|
def collect_subcommand_names(command_class)
|
|
76
100
|
names = []
|
data/lib/clamp/version.rb
CHANGED