class Clamp::Completion::BashGenerator
Generates bash shell completion scripts.
Public Class Methods
new(command_class, executable_name)
click to toggle source
# File lib/clamp/completion/bash_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/bash_generator.rb, line 15 def generate [ "# Bash completions for #{@executable_name}", "# Generated by Clamp", "", takes_value_function, "", param_count_function, "", canonical_function, "", main_function, "", "complete -F #{completion_function} #{@executable_name}" ].push("").join("\n") end
Private Instance Methods
build_case(var, indent, assign) { |cmd, has_children| ... }
click to toggle source
# File lib/clamp/completion/bash_generator.rb, line 127 def build_case(var, indent, assign) entries = {} Completion.walk_command_tree(@command_class) do |cmd, path, has_children| words = yield(cmd, has_children) next if words.nil? || words.empty? path_str = path.map { |sub| sub.names.first }.join("::") entries[path_str] = words.join(" ") end lines = ["#{indent}case \"#{var}\" in"] entries.each do |path, words| pattern = path.empty? ? '""' : "\"#{path}\"" lines << "#{indent} #{pattern})" lines << "#{indent} #{assign}($(compgen -W \"#{words}\" -- \"$cur\"))" lines << "#{indent} ;;" end lines << "#{indent}esac" lines.join("\n") end
build_lookup_function(suffix, entries, verb, default:)
click to toggle source
# File lib/clamp/completion/bash_generator.rb, line 169 def build_lookup_function(suffix, entries, verb, default:) lines = ["#{completion_function}_#{suffix}() {", ' case "$1" in'] entries.each do |key, value| pattern = key.empty? ? '""' : key lines << " #{pattern}) #{verb} #{value.is_a?(String) ? "\"#{value}\"" : value} ;;" end lines.push(" *) #{default} ;;", " esac", "}") lines.join("\n") end
canonical_function()
click to toggle source
# File lib/clamp/completion/bash_generator.rb, line 157 def canonical_function aliases = {} Completion.walk_command_tree(@command_class) do |cmd, _path, has_children| next unless has_children cmd.recognised_subcommands.each do |sub| sub.names.drop(1).each { |name| aliases[name] = sub.names.first } end end build_lookup_function("canonical", aliases, "echo", default: 'echo "$1"') end
completion_function()
click to toggle source
# File lib/clamp/completion/bash_generator.rb, line 34 def completion_function "_clamp_complete_#{Completion.encode_name(@executable_name)}" end
find_subcmd_function()
click to toggle source
# File lib/clamp/completion/bash_generator.rb, line 70 def find_subcmd_function [ "#{completion_function}_find_subcmd() {", " local i=1 word subcmd skip", " skip=$(#{completion_function}_param_count \"\")", ' while [ "$i" -lt "$COMP_CWORD" ]; do', ' word="${COMP_WORDS[$i]}"', ' case "$word" in', " -*)", " if #{completion_function}_takes_value \"$word\" \"$subcmd\"; then", " ((i++))", " fi", " ;;", " *)", find_subcmd_match_word, " ;;", " esac", " ((i++))", " done", ' echo "$subcmd $skip"', "}" ].join("\n") end
find_subcmd_match_word()
click to toggle source
# File lib/clamp/completion/bash_generator.rb, line 94 def find_subcmd_match_word subcmds = Completion.collect_subcommand_names(@command_class).join("|") [ ' if [ "$skip" -gt 0 ]; then', " ((skip--))", " else", ' case "$word" in', " #{subcmds})", " local canonical=$(#{completion_function}_canonical \"$word\")", ' if [ -z "$subcmd" ]; then', ' subcmd="$canonical"', " else", ' subcmd="${subcmd}::${canonical}"', " fi", " skip=$(#{completion_function}_param_count \"$subcmd\")", " ;;", " esac", " fi" ].join("\n") end
main_function()
click to toggle source
# File lib/clamp/completion/bash_generator.rb, line 38 def main_function [ "#{completion_function}() {", " local cur prev", " if type _init_completion &>/dev/null; then", " _init_completion", " else", ' cur="${COMP_WORDS[COMP_CWORD]}"', ' prev="${COMP_WORDS[COMP_CWORD-1]}"', " fi", "", " local subcmd_info subcmd params_remaining", " subcmd_info=$(#{completion_function}_find_subcmd)", ' subcmd="${subcmd_info%% *}"', ' params_remaining="${subcmd_info##* }"', " if #{completion_function}_takes_value \"$prev\" \"$subcmd\"; then", " return", " fi", "", ' if [[ "$cur" == -* ]]; then', options_case("$subcmd"), " fi", "", ' if [ "$params_remaining" -eq 0 ]; then', subcommands_case("$subcmd"), " fi", "}", "", find_subcmd_function ].join("\n") end
options_case(var)
click to toggle source
# File lib/clamp/completion/bash_generator.rb, line 115 def options_case(var) build_case(var, " ", "COMPREPLY=") do |cmd, _has_children| Completion.visible_options(cmd).flat_map { |o| Completion.expanded_switches(o) } end end
param_count_function()
click to toggle source
# File lib/clamp/completion/bash_generator.rb, line 147 def param_count_function entries = {} Completion.walk_command_tree(@command_class) do |cmd, path, has_children| next unless has_children entries[path.map { |s| s.names.first }.join("::")] = Completion.required_parameter_count(cmd) end build_lookup_function("param_count", entries, "echo", default: "echo 0") end
subcommands_case(var)
click to toggle source
# File lib/clamp/completion/bash_generator.rb, line 121 def subcommands_case(var) build_case(var, " ", "COMPREPLY+=") do |cmd, has_children| cmd.recognised_subcommands.flat_map(&:names) if has_children end end
takes_value_function()
click to toggle source
# File lib/clamp/completion/bash_generator.rb, line 179 def takes_value_function entries = {} Completion.walk_command_tree(@command_class) do |cmd, path, _has_children| path_str = path.map { |sub| sub.names.first }.join("::") entries[path_str] = Completion.visible_options(cmd).reject(&:flag?) .flat_map { |o| Completion.expanded_switches(o) } end lines = [ "#{completion_function}_takes_value() {", ' local option="$1"', ' local subcmd="$2"', ' case "$subcmd" in' ] entries.each do |path, switches| next if switches.empty? pattern = path.empty? ? '""' : "\"#{path}\"" lines << " #{pattern})" lines << ' case "$option" in' lines << " #{switches.join('|')}) return 0 ;;" lines << " esac" lines << " ;;" end lines.push(" esac", " return 1", "}") lines.join("\n") end