claide 0.7.0 → 0.8.0

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.
@@ -1,86 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module CLAide
4
- class Command
5
- # Provides support for the default options.
6
- #
7
- module Options
8
- # @return [Array<Array<String, String>>] The default options for a root
9
- # command implemented by CLAide.
10
- #
11
- DEFAULT_ROOT_OPTIONS = [
12
- ['--completion-script', 'Print the auto-completion script'],
13
- ['--version', 'Show the version of the tool'],
14
- ]
15
-
16
- # @return [Array<Array<String, String>>] The default options implemented
17
- # by CLAide.
18
- #
19
- DEFAULT_OPTIONS = [
20
- ['--verbose', 'Show more debugging information'],
21
- ['--no-ansi', 'Show output without ANSI codes'],
22
- ['--help', 'Show help banner of specified command'],
23
- ]
24
-
25
- # @return [Array<Array<String, String>>] The list of the default
26
- # options for the given command.
27
- #
28
- # @param [Class] command_class
29
- # The command class for which the options are needed.
30
- #
31
- def self.default_options(command_class)
32
- if command_class.root_command?
33
- Options::DEFAULT_ROOT_OPTIONS + Options::DEFAULT_OPTIONS
34
- else
35
- Options::DEFAULT_OPTIONS
36
- end
37
- end
38
-
39
- # Handles root commands options if appropriate.
40
- #
41
- # @param [Command] command
42
- # The invoked command.
43
- #
44
- # @param [ARGV] argv
45
- # The parameters of the command.
46
- #
47
- # @return [Bool] Whether any root command option was handled.
48
- #
49
- def self.handle_root_option(command, argv)
50
- argv = ARGV.coherce(argv)
51
- return false unless command.class.root_command?
52
- if argv.flag?('version')
53
- print_version(command)
54
- return true
55
- elsif argv.flag?('completion-script')
56
- print_completion_template(command)
57
- return true
58
- end
59
- false
60
- end
61
-
62
- # Prints the version of the command optionally including plugins.
63
- #
64
- # @param [Command] command
65
- # The invoked command.
66
- #
67
- def self.print_version(command)
68
- puts command.class.version
69
- if command.verbose?
70
- PluginsHelper.specifications.each do |spec|
71
- puts "#{spec.name}: #{spec.version}"
72
- end
73
- end
74
- end
75
-
76
- # Prints an auto-completion script according to the user shell.
77
- #
78
- # @param [Command] command
79
- # The invoked command.#
80
- #
81
- def self.print_completion_template(command)
82
- puts ShellCompletionHelper.completion_template(command.class)
83
- end
84
- end
85
- end
86
- end
@@ -1,47 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module CLAide
4
- class Command
5
- # Loads a command instances from arguments.
6
- #
7
- module Parser
8
- # @param [Array, ARGV] argv
9
- # A list of (remaining) parameters.
10
- #
11
- # @return [Command] An instance of the command class that was matched by
12
- # going through the arguments in the parameters and drilling down
13
- # command classes.
14
- #
15
- def self.parse(command, argv)
16
- argv = ARGV.coherce(argv)
17
- cmd = argv.arguments.first
18
- if cmd && subcommand = command.find_subcommand(cmd)
19
- argv.shift_argument
20
- parse(subcommand, argv)
21
- elsif command.abstract_command? && command.default_subcommand
22
- load_default_subcommand(command, argv)
23
- else
24
- command.new(argv)
25
- end
26
- end
27
-
28
- # @param [Array, ARGV] argv
29
- # A list of (remaining) parameters.#
30
- #
31
- # @return [Command] Returns the default subcommand initialized with the
32
- # given arguments.
33
- #
34
- def self.load_default_subcommand(command, argv)
35
- default_subcommand = command.default_subcommand
36
- subcommand = command.find_subcommand(default_subcommand)
37
- unless subcommand
38
- raise 'Unable to find the default subcommand ' \
39
- "`#{default_subcommand}` for command `#{self}`."
40
- end
41
- result = parse(subcommand, argv)
42
- result.invoked_as_default = true
43
- result
44
- end
45
- end
46
- end
47
- end
@@ -1,39 +0,0 @@
1
- # encoding: utf-8
2
-
3
- require 'claide/command/shell_completion_helper/zsh_completion_generator'
4
-
5
- module CLAide
6
- class Command
7
- module ShellCompletionHelper
8
- # Returns the completion template generated for the given command for the
9
- # given shell. If the shell is not provided it will be inferred by the
10
- # environment.
11
- #
12
- def self.completion_template(command, shell = nil)
13
- shell ||= ENV['SHELL'].split('/').last
14
- case shell
15
- when 'zsh'
16
- ZSHCompletionGenerator.generate(command)
17
- else
18
- raise Help, "Auto-completion generator for `#{shell}` shell not" \
19
- ' implemented.'
20
- end
21
- end
22
-
23
- # Indents the lines of the given string except the first one to the given
24
- # level. Uses two spaces per each level.
25
- #
26
- # @param [String] string
27
- # The string to indent.
28
- #
29
- # @param [Fixnum] indentation
30
- # The indentation amount.
31
- #
32
- # @return [String] An indented string.
33
- #
34
- def self.indent(string, indentation)
35
- string.gsub("\n", "\n#{' ' * indentation * 2}")
36
- end
37
- end
38
- end
39
- end
@@ -1,191 +0,0 @@
1
- module CLAide
2
- class Command
3
- module ShellCompletionHelper
4
- # Generates a completion script for the Z shell.
5
- #
6
- module ZSHCompletionGenerator
7
- # @return [String] The completion script.
8
- #
9
- # @param [Class] command
10
- # The command to generate the script for.
11
- #
12
- # rubocop:disable MethodLength
13
- def self.generate(command)
14
- result = <<-DOC.strip_margin('|')
15
- |#compdef #{command.command}
16
- |# setopt XTRACE VERBOSE
17
- |# vim: ft=zsh sw=2 ts=2 et
18
- |
19
- |local -a _subcommands
20
- |local -a _options
21
- |
22
- |#{case_statement_fragment(command)}
23
- DOC
24
-
25
- post_process(result)
26
- end
27
- # rubocop:enable MethodLength
28
-
29
- # Returns a case statement for a given command with the given nesting
30
- # level.
31
- #
32
- # @param [Class] command
33
- # The command to generate the fragment for.
34
- #
35
- # @param [Fixnum] nest_level
36
- # The nesting level to detect the index of the words array.
37
- #
38
- # @return [String] the case statement fragment.
39
- #
40
- # @example
41
- # case "$words[2]" in
42
- # spec-file)
43
- # [..snip..]
44
- # ;;
45
- # *) # bin
46
- # _subcommands=(
47
- # "spec-file:"
48
- # )
49
- # _describe -t commands "bin subcommands" _subcommands
50
- # _options=(
51
- # "--completion-script:Print the auto-completion script"
52
- # "--help:Show help banner of specified command"
53
- # "--verbose:Show more debugging information"
54
- # "--version:Show the version of the tool"
55
- # )
56
- # _describe -t options "bin options" _options
57
- # ;;
58
- # esac
59
- #
60
- # rubocop:disable MethodLength
61
- def self.case_statement_fragment(command, nest_level = 0)
62
- entries = case_statement_entries_fragment(command, nest_level + 1)
63
- subcommands = subcommands_fragment(command)
64
- options = options_fragment(command)
65
-
66
- result = <<-DOC.strip_margin('|')
67
- |case "$words[#{nest_level + 2}]" in
68
- | #{ShellCompletionHelper.indent(entries, 1)}
69
- | *) # #{command.full_command}
70
- | #{ShellCompletionHelper.indent(subcommands, 2)}
71
- | #{ShellCompletionHelper.indent(options, 2)}
72
- | ;;
73
- |esac
74
- DOC
75
- result.gsub(/\n *\n/, "\n").chomp
76
- end
77
- # rubocop:enable MethodLength
78
-
79
- # Returns a case statement for a given command with the given nesting
80
- # level.
81
- #
82
- # @param [Class] command
83
- # The command to generate the fragment for.
84
- #
85
- # @param [Fixnum] nest_level
86
- # The nesting level to detect the index of the words array.
87
- #
88
- # @return [String] the case statement fragment.
89
- #
90
- # @example
91
- # repo)
92
- # case "$words[5]" in
93
- # *) # bin spec-file lint
94
- # _options=(
95
- # "--help:Show help banner of specified command"
96
- # "--only-errors:Skip warnings"
97
- # "--verbose:Show more debugging information"
98
- # )
99
- # _describe -t options "bin spec-file lint options" _options
100
- # ;;
101
- # esac
102
- # ;;
103
- #
104
- def self.case_statement_entries_fragment(command, nest_level)
105
- subcommands = command.subcommands_for_command_lookup
106
- subcommands.sort_by(&:name).map do |subcommand|
107
- subcase = case_statement_fragment(subcommand, nest_level)
108
- <<-DOC.strip_margin('|')
109
- |#{subcommand.command})
110
- | #{ShellCompletionHelper.indent(subcase, 1)}
111
- |;;
112
- DOC
113
- end.join("\n")
114
- end
115
-
116
- # Returns the fragment of the subcommands array.
117
- #
118
- # @param [Class] command
119
- # The command to generate the fragment for.
120
- #
121
- # @return [String] The fragment.
122
- #
123
- def self.subcommands_fragment(command)
124
- subcommands = command.subcommands_for_command_lookup
125
- list = subcommands.sort_by(&:name).map do |subcommand|
126
- "\"#{subcommand.command}:#{subcommand.summary}\""
127
- end
128
- describe_fragment(command, 'subcommands', 'commands', list)
129
- end
130
-
131
- # Returns the fragment of the options array.
132
- #
133
- # @param [Class] command
134
- # The command to generate the fragment for.
135
- #
136
- # @return [String] The fragment.
137
- #
138
- def self.options_fragment(command)
139
- list = command.options.sort_by(&:first).map do |option|
140
- "\"#{option[0]}:#{option[1]}\""
141
- end
142
- describe_fragment(command, 'options', 'options', list)
143
- end
144
-
145
- # Returns the fragment for a list of completions and the ZSH
146
- # `_describe` function.
147
- #
148
- # @param [Class] command
149
- # The command to generate the fragment for.
150
- #
151
- # @param [String] name
152
- # The name of the list.
153
- #
154
- # @param [Class] tag
155
- # The ZSH tag to use (e.g. command or option).
156
- #
157
- # @param [Array<String>] list
158
- # The list of the entries.
159
- #
160
- # @return [String] The fragment.
161
- #
162
- def self.describe_fragment(command, name, tag, list)
163
- if list && !list.empty?
164
- <<-DOC.strip_margin('|')
165
- |_#{name}=(
166
- | #{ShellCompletionHelper.indent(list.join("\n"), 1)}
167
- |)
168
- |_describe -t #{tag} "#{command.full_command} #{name}" _#{name}
169
- DOC
170
- else
171
- ''
172
- end
173
- end
174
-
175
- # Post processes a script to remove any artifact and escape any needed
176
- # character.
177
- #
178
- # @param [String] string
179
- # The string to post process.
180
- #
181
- # @return [String] The post processed script.
182
- #
183
- def self.post_process(string)
184
- string.gsub!(/\n *\n/, "\n\n")
185
- string.gsub!(/`/, '\\\`')
186
- string
187
- end
188
- end
189
- end
190
- end
191
- end
@@ -1,102 +0,0 @@
1
- # encoding: utf-8
2
-
3
- module CLAide
4
- class Command
5
- module ValidationHelper
6
- # @return [String] Returns a message including a suggestion for the given
7
- # unrecognized arguments.
8
- #
9
- # @param [Array<String>] arguments
10
- # The unrecognized arguments.
11
- #
12
- # @param [Class] command_class
13
- # The class of the command which encountered the unrecognized
14
- # arguments.
15
- #
16
- def self.argument_suggestion(arguments, command_class)
17
- string = arguments.first
18
- type = ARGV::Parser.argument_type(string)
19
- list = suggestion_list(command_class, type)
20
- suggestion = ValidationHelper.suggestion(string, list)
21
- suggestion_message(suggestion, type, string)
22
- end
23
-
24
- # @return [Array<String>] The list of the valid arguments for a command
25
- # according to the type of the argument.
26
- #
27
- # @param [Command] command_class
28
- # The class of the command for which the list of arguments is
29
- # needed.
30
- #
31
- # @param [Symbol] type
32
- # The type of the argument.
33
- #
34
- def self.suggestion_list(command_class, type)
35
- case type
36
- when :option, :flag
37
- command_class.options.map(&:first)
38
- when :arg
39
- command_class.subcommands_for_command_lookup.map(&:command)
40
- end
41
- end
42
-
43
- # Returns a suggestion for a string from a list of possible elements.
44
- #
45
- # @return [String] string
46
- # The string for which the suggestion is needed.
47
- #
48
- # @param [Array<String>] list
49
- # The list of the valid elements
50
- #
51
- def self.suggestion(string, list)
52
- sorted = list.sort_by do |element|
53
- Helper.levenshtein_distance(string, element)
54
- end
55
- sorted.first
56
- end
57
-
58
- # @return [String] Returns a message including a suggestion for the given
59
- # suggestion.
60
- #
61
- # @param [String, Nil] suggestion
62
- # The suggestion.
63
- #
64
- # @param [Symbol] type
65
- # The type of the suggestion.
66
- #
67
- # @param [String] string
68
- # The unrecognized string.
69
- #
70
- def self.suggestion_message(suggestion, type, string)
71
- string_type = type == :arg ? 'command' : 'option'
72
- if suggestion
73
- pretty_suggestion = prettify_validation_suggestion(suggestion, type)
74
- "Unknown #{string_type}: `#{string}`\n" \
75
- "Did you mean: #{pretty_suggestion}"
76
- else
77
- "Unknown #{string_type}: `#{string}`"
78
- end
79
- end
80
-
81
- # Prettifies the given validation suggestion according to the type.
82
- #
83
- # @param [String] suggestion
84
- # The suggestion to prettify.
85
- #
86
- # @param [Type] type
87
- # The type of the suggestion: either `:command` or `:option`.
88
- #
89
- # @return [String] A handsome suggestion.
90
- #
91
- def self.prettify_validation_suggestion(suggestion, type)
92
- case type
93
- when :option, :flag
94
- suggestion = "#{suggestion}"
95
- suggestion.ansi.blue
96
- when :arg
97
- suggestion.ansi.green
98
- end
99
- end
100
- end
101
- end
102
- end