class Clamp::Completion::ZshGenerator
Generates zsh shell completion scripts.
Public Class Methods
new(command_class, executable_name)
click to toggle source
# File lib/clamp/completion/zsh_generator.rb, line 10 def initialize(command_class, executable_name) @command_class = command_class @executable_name = executable_name end
Public Instance Methods
generate()
click to toggle source
# File lib/clamp/completion/zsh_generator.rb, line 15 def generate lines = ["#compdef #{@executable_name}", ""] generate_functions(lines, @command_class, [completion_function], Set.new) lines << completion_function lines.push("").join("\n") end
Private Instance Methods
completion_function()
click to toggle source
# File lib/clamp/completion/zsh_generator.rb, line 24 def completion_function "_clamp_complete_#{Completion.encode_name(@executable_name)}" end
escape(str)
click to toggle source
# File lib/clamp/completion/zsh_generator.rb, line 116 def escape(str) str.gsub("'", "'\\''") end
generate_arguments_call(lines, specs)
click to toggle source
# File lib/clamp/completion/zsh_generator.rb, line 63 def generate_arguments_call(lines, specs) lines << " _arguments \\" generate_spec_lines(lines, specs) end
generate_functions(lines, command_class, path, visited)
click to toggle source
# File lib/clamp/completion/zsh_generator.rb, line 28 def generate_functions(lines, command_class, path, visited) has_children = command_class.has_subcommands? && !visited.include?(command_class) visited |= [command_class] func_name = path.join("_") if has_children generate_subcommand_node(lines, command_class, path, func_name, visited) else lines << "#{func_name}() {" specs = Completion.visible_options(command_class).flat_map { |o| option_specs(o) } generate_arguments_call(lines, specs) if specs.any? lines << "}" lines << "" end end
generate_spec_lines(lines, specs)
click to toggle source
# File lib/clamp/completion/zsh_generator.rb, line 68 def generate_spec_lines(lines, specs) specs.each_with_index do |spec, i| suffix = i < specs.length - 1 ? " \\" : "" lines << " #{spec}#{suffix}" end end
generate_state_dispatch(lines, command_class, path)
click to toggle source
# File lib/clamp/completion/zsh_generator.rb, line 75 def generate_state_dispatch(lines, command_class, path) lines << " case $state in" lines << " commands)" lines << " local -a cmds" lines << " cmds=(" command_class.recognised_subcommands.each do |sub| sub.names.each do |name| lines << " '#{escape(name)}:#{escape(sub.description)}'" end end lines << " )" lines << " _describe 'command' cmds" lines << " ;;" lines << " args)" lines << " case $line[1] in" command_class.recognised_subcommands.each do |sub| sub_fn = (path + [Completion.encode_name(sub.names.first)]).join("_") pattern = sub.names.join("|") lines << " #{pattern}) #{sub_fn} ;;" end lines << " esac" lines << " ;;" lines << " esac" end
generate_subcommand_node(lines, command_class, path, func_name, visited)
click to toggle source
# File lib/clamp/completion/zsh_generator.rb, line 44 def generate_subcommand_node(lines, command_class, path, func_name, visited) lines << "#{func_name}() {" lines << " local context state state_descr line" lines << " typeset -A opt_args" lines << "" specs = Completion.visible_options(command_class).flat_map { |o| option_specs(o) } specs << "'1:command:->commands'" specs << "'*::args:->args'" lines << " _arguments -C \\" generate_spec_lines(lines, specs) lines << "" generate_state_dispatch(lines, command_class, path) lines << "}" lines << "" command_class.recognised_subcommands.each do |sub| generate_functions(lines, sub.subcommand_class, path + [Completion.encode_name(sub.names.first)], visited) end end
option_specs(option)
click to toggle source
# File lib/clamp/completion/zsh_generator.rb, line 100 def option_specs(option) expanded = Completion.expanded_switches(option) suffix = "[#{escape(option.description)}]" suffix += ":#{option.type.to_s.downcase}:" unless option.flag? exclusion = expanded.length > 1 ? "'(#{expanded.join(' ')})'" : "" short = expanded.find { |s| s.match?(/^-[^-]/) } longs = expanded.grep(/^--/) if short && longs.length == 1 # Braces outside quotes for zsh brace expansion ["#{exclusion}{#{short},#{longs.first}}'#{suffix}'"] else expanded.map { |sw| "#{exclusion}'#{sw}#{suffix}'" } end end