Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/f/completion

Easy Command Line Completion for Crystal
https://github.com/f/completion

Last synced: about 2 months ago
JSON representation

Easy Command Line Completion for Crystal

Awesome Lists containing this project

README

        

# 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`:

```yaml
dependencies:
completion:
github: f/completion
```

![Completion](http://i.imgur.com/UVh97ZO.gif)

## Usage

### 1 Minute Start

First, create a file with simple autocompletion logic.
```crystal
# hello.cr
require "completion"

completion :where do |comp|
comp.on(:where) do
comp.reply ["world", "mars", "pluton"]
end
end
```

Build and try:

```bash
crystal build hello.cr
eval "$(./hello --completion --development)"

./hello
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
```crystal
require "completion"

# [detected program]

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.

```crystal
require "completion"

# myprogram
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.

```crystal
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
google bing

$ myapp goog
google

$ myapp google
google.com/search google.com/images
```

### End of Arguments

You can define what to show when arguments are finished:

```crystal
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.

```crystal
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.

```crystal
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
--help --to --upcase -h -t -u

$ myapp --
--help --to --upcase

$ myapp --help --
--help --to --upcase
```

## Installation

*(You should add these instructions to your project's README)*

```bash
# 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](https://github.com/porras/tlcr) | [src/tlcr/completion.cr](https://github.com/porras/tlcr/blob/master/src/tlcr/completion.cr#L28-L39) |
| [cryload](https://github.com/sdogruyol/cryload) | [src/cryload/cli.cr](https://github.com/sdogruyol/cryload/blob/master/src/cryload/cli.cr#L36) |

## Branched Autocompletion

This is how you can branch options and suboptions by using `values` parameter.

```crystal
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`.

```crystal
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
cards coffeepad completion cryload crystal-kemal-todo-list crystal-weekly
```

## TODO

- [ ] Add ZSH Support

## 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

- [f](https://github.com/f) Fatih Kadir Akın - creator, maintainer

> It's acutally a port of [`omelette`](http://github.com/f/omelette) package of Node.