From 3e7c0fbda0e2c9dbb56ccfcbb42fd58569fbf7ba Mon Sep 17 00:00:00 2001 From: Travis Swicegood Date: Fri, 4 May 2012 16:30:22 -0500 Subject: [PATCH] Update to @erichs' latest version of Composure Was having issues with iTerm and composure's old binding of edit-and-execute. This version removes that, so just updated rather than edit. --- lib/composure.sh | 381 +++++++++++++++++++++++++++-------------------- 1 file changed, 219 insertions(+), 162 deletions(-) mode change 100755 => 100644 lib/composure.sh diff --git a/lib/composure.sh b/lib/composure.sh old mode 100755 new mode 100644 index b4b740b..1bbf400 --- a/lib/composure.sh +++ b/lib/composure.sh @@ -1,193 +1,250 @@ -#!/bin/bash -# Composure - don't fear the UNIX chainsaw... -# by erichs, 2012 +# composure - by erichs +# light-hearted shell functions for intuitive shell programming -# these are a set of light-hearted shell functions that aim to make -# programming the shell easier and more intuitive +# install: source this script in your ~/.profile or ~/.${SHELL}rc script # latest source available at http://git.io/composure +# known to work on bash, zsh, and ksh93 -source_composure () -{ - if [ -z "$EDITOR" ] - then - export EDITOR=vi - fi - - if $(tty -s) # is this a TTY? - then - bind '"\C-j": edit-and-execute-command' - fi - - cite () - { - about () { :; } - about creates a new meta keyword for use in your functions - local keyword=$1 - for keyword in $*; do - eval "function $keyword { :; }" - done - } - - cite about param example - - draft () - { - about wraps last command into a new function - param 1: name to give function - example $ ls - example $ draft list - example $ list - local name=$1 - eval 'function ' $name ' { ' $(fc -ln -1) '; }' - } - - write () - { - about prints function declaration to stdout - param name of function or functions, separated by spaces - example $ write myfunction - example $ write func1 func2 func3 > ~/funcs.sh - local func - for func in $* - do - # trim trailing semicolons generated by declare -f - declare -f $func | sed "s/^\(.*\);$/\1/" - echo - done - } - - revise () - { - about loads function into editor for revision - param name of function or functions, separated by spaces - example $ revise myfunction - example $ revise func1 func2 func3 - local temp=$(mktemp /tmp/revise.XXXX) - write $* > $temp - $EDITOR $temp - eval "$(cat $temp)" - rm $temp - } - - metafor () - { - about prints function metadata associated with keyword - param 1: function name - param 2: meta keyword - example $ metafor reference example - local func=$1 keyword=$2 - write $func | sed -n "s/^ *$keyword \([^([].*\)$/\1/p" - } - - reference () - { - about displays help summary for all functions, or help for specific function - param 1: optional, function name - example $ reference - example $ reference metafor - - printline () - { - local metadata=$1 lhs=${2:- } - - if [[ -z "$metadata" ]] - then - return - fi +# define default metadata keywords: +about () { :; } +group () { :; } +param () { :; } +author () { :; } +example () { :; } - OLD=$IFS; IFS=$'\n' - local line - for line in $metadata - do - printf "%-20s%s\n" $lhs $line - done - IFS=$OLD - } - - help () - { - local func=$1 +cite () +{ + about creates a new meta keyword for use in your functions + param 1: keyword + example $ cite url + example $ url http://somewhere.com + group composure + + # this is the storage half of the 'metadata' system: + # we create dynamic metadata keywords with function wrappers around + # the NOP command, ':' + + # anything following a keyword will get parsed as a positional + # parameter, but stay resident in the ENV. As opposed to shell + # comments, '#', which do not get parsed, thus are not available + # at runtime. + + # a BIG caveat--your metadata must be roughly parsable: do not use + # contractions, and consider single or double quoting if it contains + # non-alphanumeric characters + + typeset keyword + for keyword in $*; do + eval "function $keyword { :; }" + done +} - local about="$(metafor $func about)" - printline "$about" $func +draft () +{ + about wraps last command into a new function + param 1: name to give function + example $ ls + example $ draft list + example $ list + group composure + + typeset func=$1 + eval 'function ' $func ' { ' $(fc -ln -1) '; }' + typeset file=$(mktemp /tmp/draft.XXXX) + typeset -f $func > $file + transcribe $func $file draft + rm $file 2>/dev/null +} - local params="$(metafor $func param)" - if [[ -n "$params" ]] - then - echo "parameters:" - printline "$params" +glossary () +{ + about displays help summary for all functions, or summary for a group of functions + param 1: optional, group name + example $ glossary + example $ glossary misc + group composure + + typeset targetgroup=${1:-} + + for func in $(listfunctions); do + typeset about="$(metafor $func about)" + if [ -n "$targetgroup" ]; then + typeset group="$(metafor $func group)" + if [ "$group" != "$targetgroup" ]; then + continue # skip non-matching groups, if specified fi + fi + letterpress "$about" $func + done +} - local examples="$(metafor $func example)" - if [[ -n "$examples" ]] - then - echo "examples:" - printline "$examples" - fi +letterpress () +{ + typeset metadata=$1 leftcol=${2:- } rightcol - unset printline - } + if [ -z "$metadata" ]; then + return + fi - if [[ -n "$1" ]] - then - help $1 - else - for func in $(compgen -A function); do - local about="$(metafor $func about)" - printline "$about" $func - done - fi + OLD=$IFS; IFS=$'\n' + for rightcol in $metadata; do + printf "%-20s%s\n" $leftcol $rightcol + done + IFS=$OLD +} - unset help printline - } +listfunctions () +{ + # unfortunately, there does not seem to be a easy, portable way to list just the + # names of the defined shell functions... + + # here's a hack I modified from a StackOverflow post: + # we loop over the ps listing for the current process ($$), and print the last column (CMD) + # stripping any leading hyphens bash sometimes throws in there + + typeset x ans + typeset this=$(for x in $(ps -p $$); do ans=$x; done; printf "%s\n" $ans | sed 's/^-*//') + case "$this" in + bash) + typeset -F | awk '{print $3}' + ;; + *) + # trim everything following '()' in ksh + typeset +f | sed 's/().*$//' + ;; + esac +} +metafor () +{ + about prints function metadata associated with keyword + param 1: function name + param 2: meta keyword + example $ metafor glossary example + group composure + typeset func=$1 keyword=$2 + + # this sed-fu is the retrieval half of the 'metadata' system: + # first 'cat' the function definition, + # then 'grep' for the metadata keyword, and + # then parse and massage the matching line + typeset -f $func | sed -n "s/;$//;s/^[ ]*$keyword \([^([].*\)*$/\1/p" } -install_composure () +reference () { - echo 'stay calm. installing composure elements...' + about displays apidoc help for a specific function + param 1: function name + example $ reference revise + group composure - # find our absolute PATH - SOURCE="${BASH_SOURCE[0]}" - while [ -h "$SOURCE" ] - do - SOURCE="$(readlink "$SOURCE")" - done - DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" + typeset func=$1 - # vim: automatically chmod +x scripts with #! lines - done_previously () { [ ! -z "$(grep BufWritePost | grep bin | grep chmod)" ]; } + typeset about="$(metafor $func about)" + letterpress "$about" $func - if [ -f ~/.vimrc ] && ! $(<~/.vimrc done_previously) - then - echo 'vimrc: adding automatic chmod+x for files with shebang (#!) lines...' - echo 'au BufWritePost * if getline(1) =~ "^#!" | if getline(1) =~ "/bin/" | silent execute "!chmod a+x " | endif | endif' >> ~/.vimrc + typeset params="$(metafor $func param)" + if [ -n "$params" ]; then + printf "parameters:\n" + letterpress "$params" fi - # source this file in your startup: .bashrc, or .bash_profile - local done=0 - done_previously () { [ ! -z "$(grep source | grep $DIR | grep composure)" ]; } + typeset examples="$(metafor $func example)" + if [ -n "$examples" ]; then + printf "examples:\n" + letterpress "$examples" + fi +} - [ -f ~/.bashrc ] && $(<~/.bashrc done_previously) && done=1 - ! (($done)) && [ -f ~/.bash_profile ] && $(<~/.bash_profile done_previously) && done=1 +revise () +{ + about loads function into editor for revision + param 1: name of function + example $ revise myfunction + group composure + + typeset func=$1 + typeset temp=$(mktemp /tmp/revise.XXXX) + + # populate tempfile... + if [ -f ~/.composure/$func.inc ]; then + # ...with contents of latest git revision... + cat ~/.composure/$func.inc >> $temp + else + # ...or from ENV if not previously versioned + typeset -f $func >> $temp + fi - if ! (($done)) + if [ -z "$EDITOR" ] then - echo 'sourcing composure from .bashrc...' - echo "source $DIR/$(basename $0)" >> ~/.bashrc + typeset EDITOR=vi fi - echo 'composure installed.' + $EDITOR $temp + source $temp + + transcribe $func $temp revise + rm $temp } -if [[ "$BASH_SOURCE" == "$0" ]] -then - install_composure -else - source_composure - unset install_composure source_composure -fi +transcribe () +{ + typeset func=$1 + typeset file=$2 + typeset operation="$3" + + if git --version >/dev/null 2>&1; then + if [ -d ~/.composure ]; then + ( + cd ~/.composure + if git rev-parse 2>/dev/null; then + if [ ! -f $file ]; then + printf "%s\n" "Oops! Couldn't find $file to version it for you..." + return + fi + cp $file ~/.composure/$func.inc + git add --all . + git commit -m "$operation $func" + fi + ) + else + if [ "$USE_COMPOSURE_REPO" = "0" ]; then + return # if you say so... + fi + printf "%s\n" "I see you don't have a ~/.composure repo..." + typeset input + typeset valid=0 + while [ $valid != 1 ]; do + printf "\n%s" 'would you like to create one? y/n: ' + read input + case $input in + y|yes|Y|Yes|YES) + ( + echo 'creating git repository for your functions...' + mkdir ~/.composure + cd ~/.composure + git init + echo "composure stores your function definitions here" > README.txt + git add README.txt + git commit -m 'initial commit' + ) + # if at first you don't succeed... + transcribe "$func" "$file" "$operation" + valid=1 + ;; + n|no|N|No|NO) + printf "%s\n" "ok. add 'export USE_COMPOSURE_REPO=0' to your startup script to disable this message." + valid=1 + ;; + *) + printf "%s\n" "sorry, didn't get that..." + ;; + esac + done + fi + fi +} : <