Travis Swicegood | 3e7c0fb | 2012-05-04 16:30:22 -0500 | [diff] [blame] | 1 | # composure - by erichs |
| 2 | # light-hearted shell functions for intuitive shell programming |
Erich Smith | a8fcf9f | 2012-04-27 15:07:04 -0400 | [diff] [blame] | 3 | |
Travis Swicegood | 3e7c0fb | 2012-05-04 16:30:22 -0500 | [diff] [blame] | 4 | # install: source this script in your ~/.profile or ~/.${SHELL}rc script |
Erich Smith | a8fcf9f | 2012-04-27 15:07:04 -0400 | [diff] [blame] | 5 | |
| 6 | # latest source available at http://git.io/composure |
Travis Swicegood | 3e7c0fb | 2012-05-04 16:30:22 -0500 | [diff] [blame] | 7 | # known to work on bash, zsh, and ksh93 |
Erich Smith | a8fcf9f | 2012-04-27 15:07:04 -0400 | [diff] [blame] | 8 | |
Travis Swicegood | 3e7c0fb | 2012-05-04 16:30:22 -0500 | [diff] [blame] | 9 | # define default metadata keywords: |
| 10 | about () { :; } |
| 11 | group () { :; } |
| 12 | param () { :; } |
| 13 | author () { :; } |
| 14 | example () { :; } |
| 15 | |
| 16 | cite () |
Erich Smith | a8fcf9f | 2012-04-27 15:07:04 -0400 | [diff] [blame] | 17 | { |
Travis Swicegood | 3e7c0fb | 2012-05-04 16:30:22 -0500 | [diff] [blame] | 18 | about creates a new meta keyword for use in your functions |
| 19 | param 1: keyword |
| 20 | example $ cite url |
| 21 | example $ url http://somewhere.com |
| 22 | group composure |
| 23 | |
| 24 | # this is the storage half of the 'metadata' system: |
| 25 | # we create dynamic metadata keywords with function wrappers around |
| 26 | # the NOP command, ':' |
| 27 | |
| 28 | # anything following a keyword will get parsed as a positional |
| 29 | # parameter, but stay resident in the ENV. As opposed to shell |
| 30 | # comments, '#', which do not get parsed, thus are not available |
| 31 | # at runtime. |
| 32 | |
| 33 | # a BIG caveat--your metadata must be roughly parsable: do not use |
| 34 | # contractions, and consider single or double quoting if it contains |
| 35 | # non-alphanumeric characters |
| 36 | |
| 37 | typeset keyword |
| 38 | for keyword in $*; do |
| 39 | eval "function $keyword { :; }" |
| 40 | done |
| 41 | } |
| 42 | |
| 43 | draft () |
| 44 | { |
| 45 | about wraps last command into a new function |
| 46 | param 1: name to give function |
| 47 | example $ ls |
| 48 | example $ draft list |
| 49 | example $ list |
| 50 | group composure |
| 51 | |
| 52 | typeset func=$1 |
| 53 | eval 'function ' $func ' { ' $(fc -ln -1) '; }' |
| 54 | typeset file=$(mktemp /tmp/draft.XXXX) |
| 55 | typeset -f $func > $file |
| 56 | transcribe $func $file draft |
| 57 | rm $file 2>/dev/null |
| 58 | } |
| 59 | |
| 60 | glossary () |
| 61 | { |
| 62 | about displays help summary for all functions, or summary for a group of functions |
| 63 | param 1: optional, group name |
| 64 | example $ glossary |
| 65 | example $ glossary misc |
| 66 | group composure |
| 67 | |
| 68 | typeset targetgroup=${1:-} |
| 69 | |
| 70 | for func in $(listfunctions); do |
| 71 | typeset about="$(metafor $func about)" |
| 72 | if [ -n "$targetgroup" ]; then |
| 73 | typeset group="$(metafor $func group)" |
| 74 | if [ "$group" != "$targetgroup" ]; then |
| 75 | continue # skip non-matching groups, if specified |
| 76 | fi |
| 77 | fi |
| 78 | letterpress "$about" $func |
| 79 | done |
| 80 | } |
| 81 | |
| 82 | letterpress () |
| 83 | { |
| 84 | typeset metadata=$1 leftcol=${2:- } rightcol |
| 85 | |
| 86 | if [ -z "$metadata" ]; then |
| 87 | return |
| 88 | fi |
| 89 | |
| 90 | OLD=$IFS; IFS=$'\n' |
| 91 | for rightcol in $metadata; do |
| 92 | printf "%-20s%s\n" $leftcol $rightcol |
| 93 | done |
| 94 | IFS=$OLD |
| 95 | } |
| 96 | |
| 97 | listfunctions () |
| 98 | { |
| 99 | # unfortunately, there does not seem to be a easy, portable way to list just the |
| 100 | # names of the defined shell functions... |
| 101 | |
| 102 | # here's a hack I modified from a StackOverflow post: |
| 103 | # we loop over the ps listing for the current process ($$), and print the last column (CMD) |
| 104 | # stripping any leading hyphens bash sometimes throws in there |
| 105 | |
| 106 | typeset x ans |
| 107 | typeset this=$(for x in $(ps -p $$); do ans=$x; done; printf "%s\n" $ans | sed 's/^-*//') |
| 108 | case "$this" in |
| 109 | bash) |
| 110 | typeset -F | awk '{print $3}' |
| 111 | ;; |
| 112 | *) |
| 113 | # trim everything following '()' in ksh |
| 114 | typeset +f | sed 's/().*$//' |
| 115 | ;; |
| 116 | esac |
| 117 | } |
| 118 | |
| 119 | metafor () |
| 120 | { |
| 121 | about prints function metadata associated with keyword |
| 122 | param 1: function name |
| 123 | param 2: meta keyword |
| 124 | example $ metafor glossary example |
| 125 | group composure |
| 126 | typeset func=$1 keyword=$2 |
| 127 | |
| 128 | # this sed-fu is the retrieval half of the 'metadata' system: |
| 129 | # first 'cat' the function definition, |
| 130 | # then 'grep' for the metadata keyword, and |
| 131 | # then parse and massage the matching line |
| 132 | typeset -f $func | sed -n "s/;$//;s/^[ ]*$keyword \([^([].*\)*$/\1/p" |
| 133 | } |
| 134 | |
| 135 | reference () |
| 136 | { |
| 137 | about displays apidoc help for a specific function |
| 138 | param 1: function name |
| 139 | example $ reference revise |
| 140 | group composure |
| 141 | |
| 142 | typeset func=$1 |
| 143 | |
| 144 | typeset about="$(metafor $func about)" |
| 145 | letterpress "$about" $func |
| 146 | |
| 147 | typeset params="$(metafor $func param)" |
| 148 | if [ -n "$params" ]; then |
| 149 | printf "parameters:\n" |
| 150 | letterpress "$params" |
| 151 | fi |
| 152 | |
| 153 | typeset examples="$(metafor $func example)" |
| 154 | if [ -n "$examples" ]; then |
| 155 | printf "examples:\n" |
| 156 | letterpress "$examples" |
| 157 | fi |
| 158 | } |
| 159 | |
| 160 | revise () |
| 161 | { |
| 162 | about loads function into editor for revision |
| 163 | param 1: name of function |
| 164 | example $ revise myfunction |
| 165 | group composure |
| 166 | |
| 167 | typeset func=$1 |
| 168 | typeset temp=$(mktemp /tmp/revise.XXXX) |
| 169 | |
| 170 | # populate tempfile... |
| 171 | if [ -f ~/.composure/$func.inc ]; then |
| 172 | # ...with contents of latest git revision... |
| 173 | cat ~/.composure/$func.inc >> $temp |
| 174 | else |
| 175 | # ...or from ENV if not previously versioned |
| 176 | typeset -f $func >> $temp |
| 177 | fi |
| 178 | |
Erich Smith | a8fcf9f | 2012-04-27 15:07:04 -0400 | [diff] [blame] | 179 | if [ -z "$EDITOR" ] |
| 180 | then |
Travis Swicegood | 3e7c0fb | 2012-05-04 16:30:22 -0500 | [diff] [blame] | 181 | typeset EDITOR=vi |
Erich Smith | a8fcf9f | 2012-04-27 15:07:04 -0400 | [diff] [blame] | 182 | fi |
| 183 | |
Travis Swicegood | 3e7c0fb | 2012-05-04 16:30:22 -0500 | [diff] [blame] | 184 | $EDITOR $temp |
| 185 | source $temp |
Erich Smith | a8fcf9f | 2012-04-27 15:07:04 -0400 | [diff] [blame] | 186 | |
Travis Swicegood | 3e7c0fb | 2012-05-04 16:30:22 -0500 | [diff] [blame] | 187 | transcribe $func $temp revise |
| 188 | rm $temp |
Erich Smith | a8fcf9f | 2012-04-27 15:07:04 -0400 | [diff] [blame] | 189 | } |
| 190 | |
Travis Swicegood | 3e7c0fb | 2012-05-04 16:30:22 -0500 | [diff] [blame] | 191 | transcribe () |
Erich Smith | a8fcf9f | 2012-04-27 15:07:04 -0400 | [diff] [blame] | 192 | { |
Travis Swicegood | 3e7c0fb | 2012-05-04 16:30:22 -0500 | [diff] [blame] | 193 | typeset func=$1 |
| 194 | typeset file=$2 |
| 195 | typeset operation="$3" |
Erich Smith | a8fcf9f | 2012-04-27 15:07:04 -0400 | [diff] [blame] | 196 | |
Travis Swicegood | 3e7c0fb | 2012-05-04 16:30:22 -0500 | [diff] [blame] | 197 | if git --version >/dev/null 2>&1; then |
| 198 | if [ -d ~/.composure ]; then |
| 199 | ( |
| 200 | cd ~/.composure |
| 201 | if git rev-parse 2>/dev/null; then |
| 202 | if [ ! -f $file ]; then |
| 203 | printf "%s\n" "Oops! Couldn't find $file to version it for you..." |
| 204 | return |
| 205 | fi |
| 206 | cp $file ~/.composure/$func.inc |
| 207 | git add --all . |
| 208 | git commit -m "$operation $func" |
| 209 | fi |
| 210 | ) |
| 211 | else |
| 212 | if [ "$USE_COMPOSURE_REPO" = "0" ]; then |
| 213 | return # if you say so... |
| 214 | fi |
| 215 | printf "%s\n" "I see you don't have a ~/.composure repo..." |
| 216 | typeset input |
| 217 | typeset valid=0 |
| 218 | while [ $valid != 1 ]; do |
| 219 | printf "\n%s" 'would you like to create one? y/n: ' |
| 220 | read input |
| 221 | case $input in |
| 222 | y|yes|Y|Yes|YES) |
| 223 | ( |
| 224 | echo 'creating git repository for your functions...' |
| 225 | mkdir ~/.composure |
| 226 | cd ~/.composure |
| 227 | git init |
| 228 | echo "composure stores your function definitions here" > README.txt |
| 229 | git add README.txt |
| 230 | git commit -m 'initial commit' |
| 231 | ) |
| 232 | # if at first you don't succeed... |
| 233 | transcribe "$func" "$file" "$operation" |
| 234 | valid=1 |
| 235 | ;; |
| 236 | n|no|N|No|NO) |
| 237 | printf "%s\n" "ok. add 'export USE_COMPOSURE_REPO=0' to your startup script to disable this message." |
| 238 | valid=1 |
| 239 | ;; |
| 240 | *) |
| 241 | printf "%s\n" "sorry, didn't get that..." |
| 242 | ;; |
| 243 | esac |
| 244 | done |
| 245 | fi |
Erich Smith | a8fcf9f | 2012-04-27 15:07:04 -0400 | [diff] [blame] | 246 | fi |
Erich Smith | a8fcf9f | 2012-04-27 15:07:04 -0400 | [diff] [blame] | 247 | } |
| 248 | |
Erich Smith | a8fcf9f | 2012-04-27 15:07:04 -0400 | [diff] [blame] | 249 | : <<EOF |
| 250 | License: The MIT License |
| 251 | |
| 252 | Copyright © 2012 Erich Smith |
| 253 | |
| 254 | Permission is hereby granted, free of charge, to any person obtaining a copy of this |
| 255 | software and associated documentation files (the "Software"), to deal in the Software |
| 256 | without restriction, including without limitation the rights to use, copy, modify, |
| 257 | merge, publish, distribute, sublicense, and/or sell copies of the Software, and to |
| 258 | permit persons to whom the Software is furnished to do so, subject to the following |
| 259 | conditions: |
| 260 | |
| 261 | The above copyright notice and this permission notice shall be included in all copies |
| 262 | or substantial portions of the Software. |
| 263 | |
| 264 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, |
| 265 | INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A |
| 266 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
| 267 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF |
| 268 | CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE |
| 269 | OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
| 270 | EOF |