Giter VIP home page Giter VIP logo

cg's Introduction

cg

Guess commands to run from stdin, and print them to stdout.

Background

Few years ago I stumbled upon fpp, a nice utility that would scan its input looking for possible pathnames, and present you with a terminal UI to select which ones to open / send to an external command; in a nut-shell, the urlview for local files.

Wouldn't it be nice if there was a similar program that could guess which command to run next, by looking at its input? For example, you pipe git log -n ...'s output into it, and it would output a bunch of "git show $sha1", "git show $sha2", "git show $sha3" lines (one per log entry); or you pipe into it ps aux's output, and it would ask you which process to kill.

enters cg..

Install

Differently from the other 2-letter-named command line utilities (i.e. cb, br) I have created in the past (pure Bash scripts), cg is written in Common Lisp (stop it already!), so getting your hands on it might not be as easy as you hoped it would, especially if it's the first time you hear about Common Lisp.

Anyway, you have to options:

  • Compile binary from sources
  • Download a pre-compiled binary

Compile

  • Get yourself a SBCL -- apologies, it's the only Common Lips implementation that I tested this with, but hopefully none of those 30 locs I managed to put together will be incompatible with other Common Lisp implementations
  • Get yourself a Quicklisp
  • Clone this repo
  • Run make followed by make install

If everything run smoothly, a shiny little cg should have appeared under '$cgrepo/bin/'; if not then, you can try downloading one of the pre-compiled binaries.

Download

Each new tag ships with pre-compiled binaries for:

  • Linux (tested on: Ubuntu 18.04 x64)
  • macOS (tested on: Sierra)
  • Windwos (tested on: Cygwin, MINGW, and LWM)

You can manually download which one you need, or you can run the following:

./download

It will guess your OS, download the pre-compiled binary, place it inside 'bin/', and make it executable.

Finally run make install to install the executable globally.

Usage

$ cg -h
Guess commands to run from stdin, and print them to stdout.

Available options:
  -h, --help               print the help text and exit
  -v, --version            print the version and exit
  -d, --debug              parse the RC file and exit

Using cg is really simple: you just pipe some text into it, and it will output some commands you most likely would want to run next:

$ git l
224d33a Fix some copy-pasta (HEAD -> master, origin/master) (by Matteo Landi)
6fb3f7b Add support for multi item selection (by Matteo Landi)
56c332c Do not specify sbcl full path (by Matteo Landi)
...

$ g l | cg
git show '224d33a' # Fix some copy-pasta (HEAD -> master, origin/master) (by Matteo Landi)
git show '6fb3f7b' # Add support for multi item selection (by Matteo Landi)
git show '56c332c' # Do not specify sbcl full path (by Matteo Landi)
...

But first, you will have to teach cg how to guess commands.

While starting up, cg will try to read "~/.cgrc" looking for command guesser definitions; a guesser is defined with the DEFINE-GUESSER macro as follows:

(cg:define-guesser kill-kill9
    ("kill ([0-9]+)" (pid))
  (format NIL "kill -9 '~a'" pid))

Where:

  • the first argument is the name of the guesser -- kill-kill9, mnemonic for something that transforms kill commands into kill -9 ones)
  • the second argument is a form defining what text the guesser is able to guess commands from; the first element is a regular expression (cg uses cl-ppcre internally), while the second is another list, listing any group that you might have defined in the regular expression -- in our case we defined a single group for the Process ID to kill
  • finally, one or multiple forms, the last one of which is expected to return a string representing the guessed command -- kill -9 '$PID' in our case

Currently, cg moves to the next input line as soon as one of the defined guessers successfully guesses a command, but please note that I am still dogfooding it, and this logic as well as the DEFINE-GUESSER API might change in case I or you found it not so easy to work with.

Also, if you are curious to see how I am using cg, take a look at my .cgrc file.

Execute one of the guessed commands

So far we taught cg how to guess commands; but what about selecting one of the suggestions, and run it? Well, I did not bother implementing a Terminal UI for this; instead, I opted to ship cg with an adapter for fzf: cg-fzf.

Hopefully, it should not take you long to implement adapters for other programs (below you can find one for dmenu), but fzf is what I am using these days, so that's what I will try to support going forward.

cg-dmenu() {
  local commands

  IFS=$'\n' commands=($(cg | dmenu))
  for cmd in ${commands[@]}; do
    echo $cmd
    bash -c "$cmd"
  done
}
cg-dmenu

Extras

GNU Readline

Piping one command's output into another command is a lot of fun, but a lot of typing too.

Add the following to your '.inputrc' to bind C-G (guess what 'g' stands for...) to re-run the last command and pipe its output into into cg-fzf:

# Pipe last command to fmcp with C-G
"\C-g": "!-1 | cg-fzf\015"

For those unfamiliar with the syntax:

  • \C-g is the key shortcut
  • !-1 pulls from history the last command
  • \015 is the return key

So say you had just run git branch -v; if you then pressed C-g, readline will pass to your terminal !-1 | cfg which will then evaluate to (and run) git branch -v | cfg!

Tmux

If you use tmux, you might want to add the following to your ".tmux.conf":

tmux bind-key "g" capture-pane -J \\\; \
  save-buffer "${TMPDIR:-/tmp}/tmux-buffer" \\\; \
  delete-buffer \\\; \
  send-keys -t . " sh -c 'cat \"${TMPDIR:-/tmp}/tmux-buffer\" | cg-fzf'" Enter

Or even better, the following in case you had tmux-externalpipe installed:

set -g @externalpipe-cg-cmd      'cg-fzf'
set -g @externalpipe-cg-key      'g'

Either one will configure tmux so that, when PrefixKey + g is pressed ('g' again..seriously?!), cg-fzf is started and the content of the current pane is piped into it.

This might come in really handy when dealing with commands that do not output the same message the second time you run them (e.g. the first time you git push a branch on GitHub, it will output a URL to create a pull request for the branch; the second time however, it won't, so the readline trick I explained above won't work in that case).

Strip escape sequences (colors)

Sometimes some of your guessers would not match because the text you piped into cg contained one or more escape sequences (usually used for colors); well, in that case try to strop them out before passing control to cg.

If using bash, you could add the following to your "~/.bashrc":

function strip-sequences {
  perl -pe 's/\e\[?.*?[\@-~]//g'
}

And then you will be able to use strip-sequences as follows:

$ g l | strip-sequences | cg

Process stderr too

Sometimes programs, especially when something did not go as planned, prefer to print to stderr instead of stdout; as a consequence, some of your guessers would not work as expected when trying to pipe these commands output to cg. A good example of this, is rm(1): if you try to rm a directory which is not empty, it will bail out and print to stdout that the specified file is indeed a directory.

Anyway, the solution is pretty simple, simply merge stderr and stdout together, before invoking cg:

$ rm directory/ 2>&1 | cg

Todo

  • replace LOAD?!
  • enhance DEFINE-GUESSER to extract documentation from the definition, and...maybe run it? A la python's docstring
  • simplify cg-fzf to use xargs instead -- xargs -t -I {} bash -c {}
  • reverse output before piping it into fzf (or use fzf's --tac) so that, when using tmux, suggestions from the most recent command, would appear first (i.e. closest to fzf prompt)

cg's People

Contributors

iamfirecracker avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.