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

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

Contributing

  1. Fork it ( https://github.com/f/completion/fork )
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

Contributors

It's acutally a port of omelette package of Node.