Easy Command Line Autocompletion Helper
Completion is a Shell Completion Helper built on Crystal. It allows you to have completion muscle for your CLI apps.
Installation
Add this to your application's shard.yml
:
dependencies:
completion:
github: f/completion
Usage
1 Minute Start
First, create a file with simple autocompletion logic.
# hello.cr
require "completion"
completion :where do |comp|
comp.on(:where) do
comp.reply ["world", "mars", "pluton"]
end
end
Build and try:
crystal build hello.cr
eval "$(./hello --completion --development)"
./hello<tab><tab>
world mars pluton
Note:
--development
flag enables autocompletion for relative (./hello
) completion. While developing your application and testing your autocompletion feature, please use--development
flag beside--completion
flag.When you are available to use your app globally (e.g. via Homebrew) you should make your users to run
eval "$(yourapp --completion)"
command.
Overview
require "completion"
# [detected program] <action> <user> <remote>
completion :action, :user, :remote do |comp|
# When Program requested :action, reply the availables.
comp.on(:action) do
comp.reply ["pull", "push"]
end
# When Program requested :user, reply the availables.
comp.on(:user) do
comp.reply ["f", "sdogruyol", "askn"]
end
# When Program requested :remote, reply the availables with defined variables.
comp.on(:remote) do
comp.reply ["github/#{comp.values[:user]}", "bitbucket/#{comp.values[:user]}"]
end
# When all parameters finished, reply always...
# It is `Dir.entries Dir.current` by default.
comp.end do
comp.reply ["--force"]
end
end
Changing Program Name
It detects program name automatically. If you want to change it or you have problems with detection, you should set the first argument to program name.
require "completion"
# myprogram <action> <remote> <suboption>
completion "myprogram", :action, :remote, :suboption do |comp|
# ...
end
Defined Values, Last Word and Whole Line
The first parameter of the block you give has last_word
, line
and fragment
parameters. So you can make
your parameters more dynamic.
completion :searchengine, :url do |comp|
comp.on(:searchengine) do
comp.reply ["google", "bing"]
end
comp.on(:url) do
comp.reply ["#{comp.values[:searchengine]}.com/search", "#{comp.values[:searchengine]}.com/images"]
end
end
This will run as:
$ myapp<tab>
google bing
$ myapp goog<tab>
google
$ myapp google <tab>
google.com/search google.com/images
End of Arguments
You can define what to show when arguments are finished:
completion :first do |comp|
comp.on(:first) do
comp.reply ["any", "option"]
end
comp.end do
comp.reply ["--force", "--prune"]
end
end
Concatting Replies
You can reply more than one time. It will concat all of these.
completion :first do |comp|
comp.on(:first) do
comp.reply ["any", "option"]
comp.reply ["other", "awesome"]
comp.reply ["options", "to", "select"]
end
end
Integrating into OptionParser
Completion can parse OptionParser
arguments and it's very easy to integrate with.
Simply use complete_with
macro with the instance of OptionParser
. It will automatically
parse all the flags and add them to the suggestion list.
OptionParser.parse! do |parser|
parser.banner = "Usage: salute [arguments]"
parser.on("-u", "--upcase", "Upcases the sallute") { }
parser.on("-t NAME", "--to=NAME", "Specifies the name to salute") { }
parser.on("-h", "--help", "Show this help") { puts parser }
# Just add this macro to the OptionParser block.
complete_with parser
end
It will run as:
$ myapp<tab>
--help --to --upcase -h -t -u
$ myapp --<tab>
--help --to --upcase
$ myapp --help --<tab>
--help --to --upcase
Installation
(You should add these instructions to your project's README)
# Add this line to your .bashrc file.
eval "$(yourapp --completion)"
Examples
Examples are here to show you how to make it more functional.
Real World Examples
| Project Name | Talk is cheap, show me the code | | ------------ | ------------------------------- | | tlcr | src/tlcr/completion.cr | | cryload | src/cryload/cli.cr |
Branched Autocompletion
This is how you can branch options and suboptions by using values
parameter.
completion :action, :subaction, :subsubaction do |comp|
comp.on(:action) do
comp.reply ["pull", "log", "commit", "remote"]
end
comp.on(:subaction) do
case comp.values[:action]
when "pull"
comp.reply ["origin", "upstream"]
when "log"
comp.reply ["HEAD", "master", "develop"]
when "commit"
comp.reply ["--amend", "-m", "-am"]
end
end
comp.on(:subsubaction) do
case comp.values[:subaction]
when "origin"
comp.reply ["origin/master", "origin/upstream", "origin/patch"]
when "HEAD"
comp.reply ["~1", "~2", "~3"]
end
end
end
Remote Autocompletion
You can make a remote autocompletion using HTTP::Client
.
require "json"
require "http/client"
completion :repos do |comp|
comp.on(:repos) do
request = HTTP::Client.get "https://api.github.com/users/f/repos"
repos = JSON.parse(request.body)
repo_names = [] of JSON::Any
repos.each {|repo| repo_names << repo["name"] }
comp.reply repo_names
end
end
This will run as:
$ mygit c<tab>
cards coffeepad completion cryload crystal-kemal-todo-list crystal-weekly
TODO
- [ ] Add ZSH Support
Contributing
- Fork it ( https://github.com/f/completion/fork )
- Create your feature branch (git checkout -b my-new-feature)
- Commit your changes (git commit -am 'Add some feature')
- Push to the branch (git push origin my-new-feature)
- Create a new Pull Request
Contributors
- f Fatih Kadir Akın - creator, maintainer
It's acutally a port of
omelette
package of Node.