Completion
This guide explains how to add shell completion to commands built with samovar.
Samovar can complete command lines using the same grammar used for parsing. It can complete option flags, boolean flag variants, nested command names, option values, positional arguments, and split arguments.
Command Entry Point
Commands expose a completion entry point alongside the normal execution entry point:
Application.call(ARGV) # Parse and execute the command.
Application.complete # Print completion candidates.
complete expects the command-line arguments to be truncated to the cursor. The final argument is the token being completed. When completing after a space, pass an empty string as the final argument:
Application.complete(["serve", "--bind", ""])
Completion candidates are printed as tab-separated values:
type value description key=value
The first three fields are always the completion type, value, and description. Additional fields are optional metadata entries encoded as key=value.
Static Completions
Option flags and nested command names are completed automatically. You can add static completions for option values and positional arguments with completions:.
require "samovar"
class Serve < Samovar::Command
self.description = "Run the server."
options do
option "--format <name>", "The output format.", default: "text", completions: ["json", "text", "yaml"]
end
end
class Application < Samovar::Command
options do
option "-h/--help", "Print help."
end
nested :command, {
"serve" => Serve
}, default: "serve"
end
Examples:
Application.complete(["ser"])
# command serve Run the server.
Application.complete(["serve", "--format", "j"])
# value json
If an option has a default value, the default is offered before other value completions.
Dynamic Completions
Use a callable provider when completions depend on runtime state.
class Serve < Samovar::Command
def self.host_completions(context)
["localhost", "0.0.0.0"].select do |host|
host.start_with?(context.current)
end
end
options do
option "--bind <host>", "The bind address.", completions: method(:host_completions)
end
end
The provider receives a Samovar::Completion::Context with:
current: The token being completed.arguments: The full truncated argument list.environment: The environment hash passed tocomplete.row: The parser row whose value is being completed. This can be an option or a positional argument.
Providers can return strings, hashes, or Samovar::Completion::Suggestion instances:
option "--mode <name>", "The mode.",
completions: [
{value: "development", description: "Local development", type: :value, suffix: " "},
{value: "production", description: "Production", type: :value}
]
Path Completion
For path-like arguments, let the shell do native path expansion by using one of the native completion providers:
class Process < Samovar::Command
options do
option "--output <path>", "The output path.", completions: :path
option "--root <path>", "The root directory.", completions: :directory
end
one :input, "The input path.", completions: :file
end
Supported native providers:
:path: Complete files and directories using the shell.:file: Alias for:path.:directory: Complete directories using the shell.:executable: Complete executable commands using the shell.
Samovar does not inspect the filesystem for these providers. It emits a typed completion request, and the shell adapter translates it to native shell path completion.
For split arguments, :executable completes the command immediately after the split marker. Once a command is present, Samovar emits a delegate completion with an index metadata field. The index is the zero-based argument index where delegated completion begins.
Dedicated Completion Executable
Shell adapters call a dedicated completion executable named completion-<command>. This avoids running the normal command during completion.
For a command named falcon, provide:
bin/falcon
bin/completion-falcon
The completion executable can be very small:
#!/usr/bin/env ruby
# frozen_string_literal: true
require_relative "../lib/my/application"
My::Application.complete
When the user completes a command by path, the shell adapter resolves the completion executable next to that command:
falcon -> completion-falcon
bin/falcon -> bin/completion-falcon
./bin/falcon -> ./bin/completion-falcon
/path/falcon -> /path/completion-falcon
Installing Shell Adapters
Shell adapter generation and installation is provided by the completion gem.
Generate an adapter script:
$ completion generate --shell zsh --command falcon
Install a generic adapter script into the default directory for the current shell:
$ completion install
The generic adapter checks whether a matching completion-<command> executable exists before handling a command. You can install an adapter for a specific command instead:
$ completion install --command falcon
You can specify the shell and directory explicitly:
$ completion install --shell fish --directory ~/.config/fish/completions --command falcon
The installed adapter calls the matching completion-<command> executable when completion is requested.
Testing Completion
You can test completion directly without involving a shell:
output = StringIO.new
Application.complete(["serve", "--format", "j"], output: output)
expect(output.string).to be == "value\tjson\t\n"
For a trailing-space completion, pass an empty final token:
Application.complete(["serve", "--format", ""], output: output)