Class: GDKBox::Completion

Inherits:
Object
  • Object
show all
Defined in:
lib/gdkbox/completion.rb

Overview

Generates shell completion scripts for the gdkbox CLI.

The list of subcommands and their flags is introspected from the Thor CLI so it stays in sync automatically. Box names and skill names are completed dynamically at completion time by shelling back out to gdkbox ls and gdkbox skills.

Constant Summary collapse

SHELLS =
%w[bash zsh].freeze
BOX_COMMANDS =

Subcommands whose first positional argument is an existing box.

%w[status dispatch ssh code install-agent set-key set-git set-remote hydrate
claim release start stop rm add-skill].freeze
MARKER =

Marker identifying the rc-file block install manages.

"# gdkbox:completion"

Instance Method Summary collapse

Constructor Details

#initialize(cli_class = CLI, config: Config.new, home: Dir.home) ⇒ Completion

Returns a new instance of Completion.



20
21
22
23
24
# File 'lib/gdkbox/completion.rb', line 20

def initialize(cli_class = CLI, config: Config.new, home: Dir.home)
  @cli = cli_class
  @config = config
  @home = home
end

Instance Method Details

#install(shell) ⇒ Object

Make completion permanent for the given shell: write the script to a file under the gdkbox home (regenerated on every install, so re-running after an upgrade picks up new subcommands) and wire the shell's rc file to source it — once, marker-tagged, like the set-host hosts entry. Sourcing a static file keeps shell startup fast: no Ruby process runs, unlike the eval "$(gdkbox completion ...)" approach. Returns [script_path, rc_path, rc_changed].



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/gdkbox/completion.rb', line 51

def install(shell)
  content = script(shell) # validates the shell first
  @config.ensure_dirs!
  script_path = File.join(@config.home, "completion.#{shell}")
  File.write(script_path, content)

  rc = rc_path(shell)
  line = "[ -f #{script_path.inspect} ] && source #{script_path.inspect}"
  existing = File.exist?(rc) ? File.read(rc) : ""
  return [script_path, rc, false] if existing.include?(MARKER)

  File.open(rc, "a") do |f|
    f.write("\n") unless existing.empty? || existing.end_with?("\n")
    f.write("\n#{MARKER}\n#{line}\n")
  end
  [script_path, rc, true]
end

#rc_path(shell) ⇒ Object



69
70
71
# File 'lib/gdkbox/completion.rb', line 69

def rc_path(shell)
  File.join(@home, shell == "bash" ? ".bashrc" : ".zshrc")
end

#script(shell) ⇒ Object

The completion script for the given shell.



27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# File 'lib/gdkbox/completion.rb', line 27

def script(shell)
  unless SHELLS.include?(shell)
    raise Error, "Unsupported shell '#{shell}'. Supported: #{SHELLS.join(', ')}."
  end

  body = function + "\ncomplete -F _gdkbox gdkbox\n"
  return body if shell == "bash"

  # zsh runs bash-style completion functions via bashcompinit, which needs
  # the completion system loaded — initialize it if the user's zshrc has
  # not already done so (the guard keeps a second compinit from running).
  <<~ZSH + body
    whence compdef >/dev/null || { autoload -U compinit && compinit }
    autoload -U +X bashcompinit && bashcompinit
  ZSH
end