claide 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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