Module: Clamp::Completion

Defined in:
lib/clamp/completion.rb,
lib/clamp/completion/zsh_generator.rb,
lib/clamp/completion/bash_generator.rb,
lib/clamp/completion/fish_generator.rb

Overview

Shell completion script generation.

Defined Under Namespace

Classes: BashGenerator, FishGenerator, ParamsSatisfiedHelper, Wanted, ZshGenerator

Constant Summary collapse

GENERATORS =
{
  bash: Clamp::Completion::BashGenerator,
  fish: Clamp::Completion::FishGenerator,
  zsh: Clamp::Completion::ZshGenerator
}.freeze

Class Method Summary collapse

Class Method Details

.argparse_specs_for(option) ⇒ Object

Return fish argparse optspecs for an option.



86
87
88
89
90
91
92
93
94
95
96
# File 'lib/clamp/completion.rb', line 86

def argparse_specs_for(option)
  switches = expanded_switches(option)
  suffix = option.flag? ? "" : "="
  short = switches.find { |s| s.match?(/^-[^-]$/) }
  longs = switches.select { |s| s.start_with?("--") }
  if short && longs.length == 1
    ["#{short.delete_prefix('-')}/#{longs.first.delete_prefix('--')}#{suffix}"]
  else
    longs.map { |l| "#{l.delete_prefix('--')}#{suffix}" }
  end
end

.collect_subcommand_names(command_class) ⇒ Object

Collect all subcommand names across the command tree.



99
100
101
102
103
104
105
# File 'lib/clamp/completion.rb', line 99

def collect_subcommand_names(command_class)
  names = []
  walk_command_tree(command_class) do |cmd, _path, has_children|
    cmd.recognised_subcommands.each { |sub| names.concat(sub.names) } if has_children
  end
  names.uniq
end

.encode_name(name) ⇒ Object

Encode a name for use as a shell function identifier. Special characters are replaced with _XX hex codes.



38
39
40
# File 'lib/clamp/completion.rb', line 38

def encode_name(name)
  name.gsub(/[^a-zA-Z0-9_]/) { |c| format("_%02x", c.ord) }
end

.expanded_switches(option) ⇒ Object

Return switches with --[no-]foo expanded to --foo and --no-foo.



50
51
52
53
54
55
56
57
58
# File 'lib/clamp/completion.rb', line 50

def expanded_switches(option)
  option.switches.flat_map do |switch|
    if switch =~ /^--\[no-\](.*)/
      ["--#{Regexp.last_match(1)}", "--no-#{Regexp.last_match(1)}"]
    else
      switch
    end
  end
end

.generate(command_class, shell, executable_name) ⇒ Object



42
43
44
45
46
47
# File 'lib/clamp/completion.rb', line 42

def generate(command_class, shell, executable_name)
  generator_class = GENERATORS.fetch(shell) do
    raise ArgumentError, "unsupported shell: #{shell.inspect}"
  end
  generator_class.new(command_class, executable_name).generate
end

.required_parameter_count(command_class) ⇒ Object

Count required, non-multivalued parameters for a command.



81
82
83
# File 'lib/clamp/completion.rb', line 81

def required_parameter_count(command_class)
  command_class.parameters.count { |p| p.required? && !p.multivalued? }
end

.visible_options(command_class) ⇒ Object

Options visible in completion (excludes hidden).



61
62
63
# File 'lib/clamp/completion.rb', line 61

def visible_options(command_class)
  command_class.recognised_options.reject(&:hidden?)
end

.walk_command_tree(command_class, path = [], visited = Set.new) {|command_class, path, has_children| ... } ⇒ Object

Walk the command tree depth-first, yielding (command_class, path, has_children). Path is an array of Subcommand::Definition objects. Always yields, even for revisited classes (with has_children=false).

Yields:

  • (command_class, path, has_children)


68
69
70
71
72
73
74
75
76
77
78
# File 'lib/clamp/completion.rb', line 68

def walk_command_tree(command_class, path = [], visited = Set.new, &block)
  fresh = !visited.include?(command_class)
  visited |= [command_class]
  has_children = command_class.has_subcommands? && fresh
  yield command_class, path, has_children
  return unless has_children

  command_class.recognised_subcommands.each do |sub|
    walk_command_tree(sub.subcommand_class, path + [sub], visited, &block)
  end
end