Update to @erichs' latest version of Composure
authorTravis Swicegood <development@domain51.com>
Fri, 4 May 2012 21:30:22 +0000 (16:30 -0500)
committerTravis Swicegood <development@domain51.com>
Fri, 4 May 2012 21:30:22 +0000 (16:30 -0500)
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 [changed mode: 0755->0644]

old mode 100755 (executable)
new mode 100644 (file)
index b4b740b..1bbf400
-#!/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 <afile>" | 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
+}
 
 : <<EOF
 License: The MIT License