360 lines
8.3 KiB
Markdown
360 lines
8.3 KiB
Markdown
|
<img src="https://rawgit.com/f/omelette/master/resources/logo.svg?v1" height="80">
|
||
|
|
||
|
> Omelette is a simple, template based autocompletion tool for Node projects with super easy API.
|
||
|
|
||
|
[![npm version](https://badge.fury.io/js/omelette.svg)](https://badge.fury.io/js/omelette)
|
||
|
[![Build Status](https://travis-ci.org/f/omelette.svg?branch=master)](https://travis-ci.org/f/omelette)
|
||
|
|
||
|
```bash
|
||
|
yarn add omelette
|
||
|
# or
|
||
|
npm install omelette
|
||
|
```
|
||
|
|
||
|
You just have to decide your program name and CLI fragments.
|
||
|
|
||
|
```javascript
|
||
|
omelette`github ${['pull', 'push']} ${['origin', 'upstream']} ${['master', 'develop']}`.init()
|
||
|
```
|
||
|
|
||
|
And you are almost done! The output will be like this:
|
||
|
|
||
|
<img src="https://raw.github.com/f/omelette/master/resources/omelette-new.gif?v1" width="640">
|
||
|
|
||
|
## Quick Start
|
||
|
|
||
|
Implementing omelette is very easy.
|
||
|
|
||
|
```javascript
|
||
|
import * as omelette from 'omelette';
|
||
|
|
||
|
const firstArgument = ({ reply }) => {
|
||
|
reply([ 'beautiful', 'cruel', 'far' ])
|
||
|
}
|
||
|
|
||
|
const planet = ({ reply }) => {
|
||
|
reply([ 'world', 'mars', 'pluto' ])
|
||
|
}
|
||
|
|
||
|
omelette`hello|hi ${firstArgument} ${planet}`.init()
|
||
|
```
|
||
|
|
||
|
<img src="https://raw.github.com/f/omelette/master/resources/omelette-new-hello.gif?v1" width="640">
|
||
|
|
||
|
### Simple Event Based API ☕️
|
||
|
|
||
|
It's based on a simple CLI template.
|
||
|
|
||
|
Let's think we have a executable file with the name **githubber**, *in a global path*.
|
||
|
|
||
|
And in our program, code will be:
|
||
|
|
||
|
```javascript
|
||
|
import * as omelette from 'omelette';
|
||
|
|
||
|
// Write your CLI template.
|
||
|
const completion = omelette(`githubber|gh <action> <user> <repo>`);
|
||
|
|
||
|
// Bind events for every template part.
|
||
|
completion.on('action', ({ reply }) => {
|
||
|
reply([ 'clone', 'update', 'push' ])
|
||
|
})
|
||
|
|
||
|
completion.on('user', ({ reply }) => {
|
||
|
reply(fs.readdirSync('/Users/'))
|
||
|
})
|
||
|
|
||
|
completion.on('repo', ({ before, reply }) => {
|
||
|
reply([
|
||
|
`http://github.com/${before}/helloworld`,
|
||
|
`http://github.com/${before}/blabla`
|
||
|
])
|
||
|
})
|
||
|
|
||
|
// Initialize the omelette.
|
||
|
completion.init()
|
||
|
|
||
|
// If you want to have a setup feature, you can use `omeletteInstance.setupShellInitFile()` function.
|
||
|
if (~process.argv.indexOf('--setup')) {
|
||
|
completion.setupShellInitFile()
|
||
|
}
|
||
|
|
||
|
// Rest is yours
|
||
|
console.log("Your program's default workflow.")
|
||
|
console.log(process.argv)
|
||
|
```
|
||
|
|
||
|
`complete.reply` is the completion replier. You must pass the options into that method.
|
||
|
|
||
|
### ES6 Template Literal API 🚀
|
||
|
|
||
|
You can use **Template Literals** to define your completion with a simpler (super easy) API.
|
||
|
|
||
|
```javascript
|
||
|
import * as omelette from 'omelette';
|
||
|
|
||
|
// Just pass a template literal to use super easy API.
|
||
|
omelette`hello ${[ 'cruel', 'nice' ]} ${[ 'world', 'mars' ]}`.init()
|
||
|
```
|
||
|
|
||
|
Let's make the example above with ES6 TL:
|
||
|
|
||
|
```javascript
|
||
|
import * as omelette from 'omelette'
|
||
|
|
||
|
// Write your CLI template.
|
||
|
omelette`
|
||
|
githubber|gh
|
||
|
|
||
|
${[ 'clone', 'update', 'push' ]}
|
||
|
${() => fs.readdirSync('/Users/')}
|
||
|
${({ before }) => [
|
||
|
`http://github.com/${before}/helloworld`,
|
||
|
`http://github.com/${before}/blabla`,
|
||
|
]}
|
||
|
`.init()
|
||
|
```
|
||
|
|
||
|
Also you can still use lambda functions to make more complex template literals:
|
||
|
|
||
|
#### Advanced Template Literals
|
||
|
|
||
|
```javascript
|
||
|
import * as omelette from 'omelette';
|
||
|
|
||
|
omelette`
|
||
|
githubber|gh
|
||
|
${['pull', 'push', 'star'] /* Direct command list */}
|
||
|
${require('some/other/commands') /* Import from another file */}
|
||
|
${getFromRemote('http://api.example.com/commands') /* Remote call at the beginning */}
|
||
|
${({ reply }) => fetch('http://api.example.com/lazy-commands').then(reply) /* Fetch when argument <tab>bed */}
|
||
|
${() => fs.readdirSync("/Users/") /* Access filesystem via Node */}
|
||
|
${({ before }) => [ /* Use parameters like `before`, `line`, `fragment` or `reply` */
|
||
|
`${before}/helloworld`,
|
||
|
`${before}/blabla`
|
||
|
]}
|
||
|
`.init()
|
||
|
|
||
|
// No extra configuration required.
|
||
|
|
||
|
console.log("Your program's default workflow.")
|
||
|
console.log(process.argv)
|
||
|
```
|
||
|
|
||
|
### Tree API 🌲
|
||
|
|
||
|
You can use `simple objects` as autocompletion definitions:
|
||
|
|
||
|
```javascript
|
||
|
omelette('hello').tree({
|
||
|
cruel: ['world', 'moon'],
|
||
|
beautiful: ['mars', 'pluto']
|
||
|
}).init();
|
||
|
```
|
||
|
|
||
|
## Install
|
||
|
|
||
|
### Automated Install
|
||
|
|
||
|
Installing, and making your users install the autocompletion feature is very simple.
|
||
|
|
||
|
You can use simply use `setupShellInitFile` function.
|
||
|
|
||
|
```javascript
|
||
|
// If you want to write file,
|
||
|
complete.setupShellInitFile('~/.my_bash_profile')
|
||
|
```
|
||
|
|
||
|
If you use Bash, it will create a file at `~/.<program-name>/completion.sh` and
|
||
|
append a loader code to `~/.bash_profile` file.
|
||
|
|
||
|
If you use Zsh, it appends a loader code to `~/.zshrc` file.
|
||
|
|
||
|
If you use Fish, it appends a loader code to `~/.config/fish/config.fish` file.
|
||
|
|
||
|
*TL;DR: It does the Manual Install part, basically.*
|
||
|
|
||
|
### Manual Install
|
||
|
|
||
|
*(You should add these instructions to your project's README)*
|
||
|
|
||
|
In **zsh**, you can write these:
|
||
|
|
||
|
```bash
|
||
|
echo '. <(./githubber --completion)' >> .zshrc
|
||
|
```
|
||
|
|
||
|
In **bash**, you should write:
|
||
|
|
||
|
```bash
|
||
|
./githubber --completion >> ~/githubber.completion.sh
|
||
|
echo 'source ~/githubber.completion.sh' >> .bash_profile
|
||
|
```
|
||
|
|
||
|
In **fish**, you can write:
|
||
|
|
||
|
```bash
|
||
|
echo 'githubber --completion-fish | source' >> ~/.config/fish/config.fish
|
||
|
```
|
||
|
|
||
|
That's all!
|
||
|
|
||
|
Now you have an autocompletion system for your CLI tool.
|
||
|
|
||
|
## Additions
|
||
|
|
||
|
There are some useful additions for omelette.
|
||
|
|
||
|
|
||
|
### Parameters
|
||
|
|
||
|
Callbacks have two parameters:
|
||
|
|
||
|
- The fragment name (e.g.`command` of `<command>` template) *(only in global event)*
|
||
|
- The meta data
|
||
|
- `fragment`: The number of fragment.
|
||
|
- `before`: The previous word.
|
||
|
- `line`: The whole command line buffer allow you to parse and reply as you wish.
|
||
|
- `reply`: This is the reply function to use *this-less* API.
|
||
|
|
||
|
### Global Event
|
||
|
|
||
|
You also can be able to listen all fragments by "complete" event.
|
||
|
|
||
|
```javascript
|
||
|
complete.on('complete', (fragment, { reply }) => reply(["hello", "world"]));
|
||
|
```
|
||
|
|
||
|
### Numbered Arguments
|
||
|
|
||
|
You also can listen events by its order.
|
||
|
|
||
|
```javascript
|
||
|
complete.on('$1', ({ reply }) => reply(["hello", "world"]))
|
||
|
```
|
||
|
|
||
|
### Autocompletion Tree
|
||
|
|
||
|
You can create **completion tree** to more complex autocompletions.
|
||
|
|
||
|
```js
|
||
|
omelette('hello').tree({
|
||
|
how: {
|
||
|
much: {
|
||
|
is: {
|
||
|
this: ['car'],
|
||
|
that: ['house'],
|
||
|
}
|
||
|
}
|
||
|
are: ['you'],
|
||
|
many: ['cars', 'houses'],
|
||
|
},
|
||
|
where: {
|
||
|
are: {
|
||
|
you: ['from'],
|
||
|
the: ['houses', 'cars'],
|
||
|
},
|
||
|
is: {
|
||
|
// You can also add some logic with defining functions:
|
||
|
your() {
|
||
|
return ['house', 'car'];
|
||
|
},
|
||
|
}
|
||
|
},
|
||
|
}).init()
|
||
|
```
|
||
|
|
||
|
Now you will be able to use your completion as tree.
|
||
|
|
||
|
<img src="https://raw.github.com/f/omelette/master/resources/omelette-tree-new.gif?v1" width="640">
|
||
|
|
||
|
> Thanks [@jblandry](https://github.com/jblandry) for the idea.
|
||
|
|
||
|
#### Advanced Tree Implementations
|
||
|
|
||
|
You can seperate your autocompletion by importing objects from another file:
|
||
|
|
||
|
```js
|
||
|
omelette('hello').tree(require('./autocompletion-tree.js')).init();
|
||
|
```
|
||
|
|
||
|
### Short Names
|
||
|
|
||
|
You can set short name of an executable:
|
||
|
|
||
|
In this example, `githubber` is long and `gh` is shorter examples.
|
||
|
|
||
|
```javascript
|
||
|
omelette('githubber|gh <module> <command> <suboption>');
|
||
|
```
|
||
|
|
||
|
## Test
|
||
|
|
||
|
Now, you can try it in your shell.
|
||
|
|
||
|
```bash
|
||
|
git clone https://github.com/f/omelette
|
||
|
cd omelette/example
|
||
|
alias githubber="./githubber" # The app should be global, completion will search it on global level.
|
||
|
./githubber --setup --debug # --setup is not provided by omelette, you should proxy it.
|
||
|
# (reload bash, or source ~/.bash_profile or ~/.config/fish/config.fish)
|
||
|
omelette-debug-githubber # See Debugging section
|
||
|
githubber<tab>
|
||
|
ghb<tab> # short alias
|
||
|
gh<tab> # short alias
|
||
|
```
|
||
|
|
||
|
### Debugging
|
||
|
|
||
|
`--debug` option generates a function called `omlette-debug-<programname>`.
|
||
|
(`omlette-debug-githubber` in this example).
|
||
|
|
||
|
When you run `omlette-debug-<programname>`, it will create aliases for your
|
||
|
application. (`githubber` and `gh` in this example).
|
||
|
|
||
|
Long name,
|
||
|
|
||
|
```bash
|
||
|
$ githubber<tab>
|
||
|
clone update push
|
||
|
```
|
||
|
|
||
|
Or short name:
|
||
|
|
||
|
```bash
|
||
|
$ gh<tab>
|
||
|
clone update push
|
||
|
```
|
||
|
|
||
|
Then you can start easily.
|
||
|
|
||
|
```bash
|
||
|
$ ./githubber<tab>
|
||
|
clone update push
|
||
|
```
|
||
|
|
||
|
```bash
|
||
|
$ ./githubber cl<tab>
|
||
|
$ ./githubber clone<tab>
|
||
|
Guest fka
|
||
|
```
|
||
|
|
||
|
```bash
|
||
|
$ ./githubber clone fka<tab>
|
||
|
$ ./githubber clone fka http://github.com/fka/<tab>
|
||
|
http://github.com/fka/helloworld
|
||
|
http://github.com/fka/blabla
|
||
|
```
|
||
|
|
||
|
## Who uses?
|
||
|
|
||
|
**Windows Azure** uses Omelette to support autocompletion in [azure-cli](https://github.com/WindowsAzure/azure-sdk-tools-xplat).
|
||
|
|
||
|
## Contribute
|
||
|
|
||
|
I need your contributions to make that work better!
|
||
|
|
||
|
## License
|
||
|
|
||
|
This project licensed under MIT.
|