Ecosyste.ms: Awesome

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

Awesome Lists | Featured Topics | Projects

https://github.com/drnic/tabtab

Create and install double-tab (‘tab tab’) auto-completions for any command-line application on any shell (bash, fish, ksh, etc)
https://github.com/drnic/tabtab

Last synced: 5 days ago
JSON representation

Create and install double-tab (‘tab tab’) auto-completions for any command-line application on any shell (bash, fish, ksh, etc)

Awesome Lists containing this project

README

        

= tabtab

* http://tabtab.rubyforge.org

== DESCRIPTION:

Create and install double-tab ('tab tab') auto-completions for any
command-line application on any shell (bash, fish, ksh, etc).

When you use the command-line, you can double-tab to auto-complete the name
of a command-line application or a target file or folder. Its possible to
provide your own completions for applications: git comes with bash shell completions,
and the fish shell includes a library of completions for many applications.

=== Quick Start/Trial Me:

The tabtab gem comes with some pre-defined completions for some popular applications
that benefit from completions: rails, newgem, cucumber, github (and its alias gh).
It takes 2 minutes to trial this project and see if you like it:

bash>
sudo gem install tabtab
install_tabtab
source ~/.tabtab.bash

rails -d TABTAB

gem install github

github TABTAB
gh netTAB feTAB
gh netTAB web TABTAB

It just works. Flags. Commands. Intelligent values. Aliases. Ooh yeah.

Now, add 'source ~/.tabtab.bash' to your .bash_profile so you have this awesomeness in all your
terminal shells.

Read on to learn how to write your own auto-completions with only a few lines of Ruby...

== FEATURES/PROBLEMS:

* Completion defintions/recipes are shell agnostic (bash, fish, ksh, etc)
* Definitions are written in Ruby
* Can be bundled within RubyGems, explicitly referenced in local files, or
automatically generated from -h help output from existing applications.
* Very easy to use: 'install_tabtab' to find available completions, then 'source ~/.tabtab.bash'

== SAMPLE DEFINITION:

TabTab definitions for auto-completions are very easy to write. Initially
you can store them in a normal Ruby file (say ~/.tabtab/myapp.rb) and
later, if the application is a Ruby application and distributed as a RubyGem
you can bundle it with the distribution.

=== Flags

A sample TabTab definition for the script/server command that is found
within all Ruby on Rails applications:

TabTab::Definition.register('script/server') do |c|
c.flag :debugger, :d

c.flag :environment, :e do
Dir['config/environments/*.rb'].map { |env| env.gsub(/^.*environments\//,'').gsub(/.rb$/,'') }
end

# script/server -p TABTAB -> generated first available port 3000, 3001, 3002
c.flag :port, :p do
port = 3000
until `netstat -an | grep "^tcp" | grep #{port}`.strip.empty? || port > 3010
port += 1
end
port > 3010 ? [] : [port.to_s]
end
end

This definition defines 3 flags (each with short and long names): --debugger, --environment, and --port.
The --debugger flag is a simple autocompletion. At the command-line, if you typed "script/server --d" and
pressed double-tab it would instantly complete to "script/server --debugger ".

The other two flags can take values (e.g. "--environment development" or "--port 3000") and their
definitions are more powerful. If you double-tab after "script/server --environment " you will be
presented with options [development, test, production] for completion. If you type the first
letter, it will complete to the full value.

Similarly for "--port". The algorithm above will find the first available port number from 3000+.
As it only returns a single value in its result array, this value is automatically displayed
on the command line. Very tricky indeed.

The #flag method (and its alias #flags) takes 1+ symbols describing the flag names. Its last
argument can be a string as a description. This is not used for bash shell users, but is
available to ksh/fish users who's autocompletion systems are capable of displaying them
inline with the completion options.

The #flag method can also take a block. The result of the block must be an array of strings.
These blocks (also available to #command and #default methods below) are called 'value blocks'.
They return the complete, or a useful subset, of available values for the flag or command.

=== Commands

Many command-line apps take a command as their first argument. For example, the github CLI
has commands such as: info, pull, and network. The latter even has sub-commands.
Subsequently, you might run the following at the command line:

github info
github pull drnic
github network commits
github network web drnic

The following sample of the tabtab definition for the github command
shows how tabtab can provide autocompletions for every example above,
including the user values for the 'github pull' and 'github network web' commands.

TabTab::Definition.register('github') do |c|
def users
`github info | grep "^ -" | sed -e "s/ - //" | sed -e "s/ .*$//"`.split("\n")
end

c.command :info, "Show info"

c.command :pull, "Pull from a remote." do |pull|
pull.default { users }
pull.flag :merge
end

c.command :network, "Project network tools" do |network|
network.command(:web) { users }
network.command :commits
end
end

The #command method requires a symbol for the name, and can take a string for the
command description (see Flags section above).

The #command method can also take a value block, like #flag above. It must return
an array of strings.

The above example shows the behaviour of the 'github network web' command being abstracted
into a separate method. Similarly, this method could be defined in an external Module,
and included as necessary.

Note that 'pull.flag :merge' defines that 'github pull' can complete to 'github pull --merge'
as well as the list of values returned from the #default value block.

=== Default value blocks

In the sample github definition above, the 'c.command :pull' value block is not
the immediate block passed to the #command method. Instead the value block is
defined via 'pull.default { }' This is an alternate syntax to the earlier value block,
and is used where your command can autocomplete to various flags, sub-commands or other
from within a generated list of values (via the #default value block).

=== Value block syntax

Value blocks are normal Ruby blocks that return an array of Strings.

The following syntax options for the 'run' command are functionally equivalent:

TabTab::Definition.register('myapp') do |c|
c.command :run do
%w[things to run]
end

c.command :run, "A description" do
%w[things to run]
end

c.command :run do |run|
run.default do
%w[things to run]
end
end

c.command(:run, "A description") { %w[things to run] }

def things_to_run
$[things to run]
end
c.command(:run) { things_to_run }
end

=== Application name aliases

I don't ever type 'script/generate', rather I have an alias 'gen' for it.

TabTab supports user-defined aliases (you might have an alias 'g' for 'script/generate') via its
~/.tabtab.yml configuration file. See Aliases section below.

== INSTALL:

* sudo gem install tabtab

== SETUP:

Run `install_tabtab` to install the completions that come built-in with the
tabtab RubyGem, and within any other RubyGem, at the time of execution.

In your .bash_profile add:

source ~/.tabtab.bash

=== Future shells:

In your .fish_profile add:

source ~/.tabtab.fish

In your .ksh_profile add:

source ~/.tabtab.ksh

=== Re-running install_tabtab

You never need to run this command again after new RubyGem updates, but
you do need to re-run this command each time you:

* install a RubyGem that contains a tabtab_definitions.rb file for the first time
(once install_tabtab has discovered the gem it will automatically pick up
changes to the definitions in future gem versions)
* want to add completions defined in a local file, rather than bundled in a gem (see Local Files below)
* want to add completions for 'external' apps (see External below)

Each time you run install_tabtab the above ~/.tabtab.sh file(s) will be updated. You then need to restart
your shell(s) to load the new completions, or run the above command explicitly.

=== RubyGems: Completion Definitions in RubyGems

By default, install_tabtab automatically finds any completion definitions bundled in RubyGems.
The tabtab gem itself includes lots of definitions for applications such as rails, script/server,
rake, gem, cucumber, github, and others.

In time, the development and deployment of each
script may be adopted by the relevant project owners and bundled in their RubyGems rather than
the tabtab gem itself.

You can bundle tabtab defintions for 1 or more applications in your own RubyGems. Typically,
you will include tabtab defintions for the applications that you bundle with your gem. For example,
the global 'rails' app and the per-rails project 'script/server' applications are bundled in the
'railties' gem. Rails would ideally include its tabtab definitions in this railties gem and
the core Rails team would maintain/update the tabtab definition whenever the actual
applications are updated. This way the application and the tabtab defintions will be consistent.

To include tabtab definitions in a RubyGem, you need to add a 'tabtab_definitions.rb' file
somewhere in your gem. The suggested location is 'lib/tabtab_definitions.rb'.

=== Local Files: Completion Definitions in Local Files

You can create and develop your own tabtab definitions for your apps or other people's apps.
Its really simple. Really easy.

1. Create a file
2. Include the definition code:

TabTab::Definition.register('TARGET_APP_NAME') do |c| ... end

3. Create a ~/.tabtab.yml file
4. Add file configuration:

files:
"/Users/drnic/.tabtab_definitions/TARGET_APP_NAME.rb": TARGET_APP_NAME

You can include multiple definitions in a single file (using multiple 'register(app_name)' calls)
and including a list of file names:

files:
"/Users/drnic/.tabtab_definitions/my_definitions.rb": curl, mongrel_rails

You can include multiple files for multiple applications:

files:
"/Users/drnic/.tabtab_definitions/curl.rb": curl
"/Users/drnic/.tabtab_definitions/mongrel.rb": mongrel_rails

=== External: Completions for Applications without Definitions

As a bonus feature, tabtab supports autocompletions for existing applications
without requiring a definition file. Neat! For example, the 'curl' command has lots of flag options.
For shits-and-giggles, run 'curl -h'. There are currently 56 different flag options. Fifty six!

It would be so sweet to instantly get an autocompletion for these flags without any
coding effort; no definition file.

It is as simple as adding an "external" key to your ~/.tabtab.yml config file:

external:
- curl
- cap

=== Hide small flags

Many applications have command-line flag arguments, such as --port (long) or -p (short).
The short form is useful if you are typing them yourself - they are two characters long.
If you are using auto-completions, it may not be meaningful nor useful to see the
short form flags.

You can disable short-form versions of flags via the ~/.tabtab.yml config file. Add the following line:

shortflags: disable

=== Aliases: Reusing Completions against local Aliases

TabTab supports user-defined aliases (you might have an alias 'g' for 'script/generate') via its
~/.tabtab.yml configuration file.

Use the "aliases" key to your ~/.tabtab.yml config file:

aliases:
gen: script/generate
console: script/console
ss: script/server
server: script/server

== SHELL SUPPORT:

TabTab is shell agnostic. If your shell supports auto-completions, then it should be possible
to hook in the TabTab definitions.

Yet it initially only supports Bash. Why? I know how to hook bash's completion mechanism
into an external app.

For bash's complete command, the -C option allows me to specify an external command that will
handle the request for completion options. Below, 'tabtab' is the external completion command and it will
be used for the github command.

complete -o default -C tabtab github

For bash, when tabtab is executed, its last 3 arguments are: command name, current token, previous token.
It also passes the entire current command line via $COMP_LINE.

I know that fish and ksh and other shells have sexy completion support. But I cannot figure out how
to configure them to delegate to tabtab.

Fish's complete only seems to allow calls to fish functions, and not external apps. But perhaps
the fish function can then delegate to tabtab? Perhaps I just need a _tabtab fish function, which
proxies the request through to the tabtab command?

If you know these things, or are proficient enough in your preferred shell to poke around and help out
please let me know. I have some cucumber scenarios ready and waiting for your help.

== REQUIREMENTS:

Currently, tabtab works with the bash shell, though it is designed to be
shell agnostic.

== KNOWN ISSUES/LIMITATIONS & ARCHITECTURE OVERVIEW:

Pride and ego mean this section goes at the bottom of the readme... this section includes the known
issues and limitations of TabTab due to current architectural decisions, the aim of being
shell agnostic whilst initially only implementing TabTab for bash, and DSL challenges in
attempting to support all possible command-line app APIs with a sexy, small DSL for describing
auto-completions.

=== Cannot support rake/sake/cap colon-separated completions

This will be fixed soon. I only just discovered this deficiency.

Auto-completion for rake/sake/cap - when I get it working - will allow you to tab
through the namespacing across the colons. E.g. 'rake db:test:' and tabtab will show
all the tasks within this namespace. That's what it should do. That's what all the
current implementations of auto-completion do before tabtab. But with tabtab (currently)
it can't do it. I suck. But here's the reason and the fix.

To fix it requires rewriting the guts of the tabtab application to use $COMP_LINE
environment variable instead of the current, previous token.

Currently tabtab works by passing the current and previous token in the current command
line string around. But bash isn't coping with colon's correctly. If you tabtab after
a colon it just ignores the characters before it.

So, I'll abandon the whole mechanism of (current, previous) tokens and use the $COMP_LINE
variable and manually parse it into tokens via the Shellwords.shellwords function.

=== Nested intelligent lists

Some applications may want to have multiple auto-completed values in a row, with the 2nd value's list
being dependent upon the value of the first. For example, the 'rubyforge add_release' command takes
four more arguments: group_id, package_id, release_name, userfile. The first two - group_id and package_id -
are from known lists of values. The values available for package_id are dependent upon the group_id value.
This is a nested list, and currently TabTab probably can't do them.

Currently intelligent, dynamic lists of completable values are defined via 'default' blocks - either blocks
passed to #flag or #command calls, or via #default calls. I guess to support nested lists you'd need
nested default blocks. Not sure.

Or perhaps we need specific syntax to support commands with 2+ arguments.

TabTab::Definition.register('rubyforge') do |c|
c.command(:add_release, 1) { list_of_rubyforge_group_names }
c.command(:add_release, 2) { |group_id| list_of_rubyforge_package_names_in(group_id) }
end

In this pseudo code we redefine the :add_release command for each nested argument. The 2nd+ argument
is passed the completed values from the previous arguments of the command so they
can filter their lists as appropriate. Might work.

Of course, the second argument (1, 2, ...) would be optional and only required if you were trying
to describe a nested list of interdependent values.

== SOURCE:

The source for this project is at http://github.com/drnic/tabtab

Using git, you can clone the project, run its tests and install it:

git clone git://github.com/drnic/tabtab.git
cd tabtab
bundle
rake
rake install

== ACKNOWLEDGEMENT

I wrote most of this at Railscamp #4 in Adelaide, between 13th and 17th of November 2008. Railscamps
are so awesome. They are a conference without the conference part. Coding, lightning talks, alcohol,
and guitar hero. Thanks to all the people I demo'd tabtab during its development, for their thoughts
on the final DSL, and finally for helping give TabTab a name.

== LICENSE:

(The MIT License)

Copyright (c) 2008, 2009, 2011 Dr Nic Williams (http://drnicwilliams.com)

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.