added some more stuff and changed prompt
This commit is contained in:
parent
cb62f96109
commit
ae447242b5
11 changed files with 3181 additions and 1 deletions
675
bin/ansi
Executable file
675
bin/ansi
Executable file
|
@ -0,0 +1,675 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# ANSI code generator
|
||||
#
|
||||
# © Copyright 2015 Tyler Akins
|
||||
# Licensed under the MIT license with an additional non-advertising clause
|
||||
# See http://github.com/fidian/ansi
|
||||
|
||||
ansi::addCode() {
|
||||
local N
|
||||
|
||||
if [[ "$1" == *=* ]]; then
|
||||
N="${1#*=}"
|
||||
N="${N//,/;}"
|
||||
else
|
||||
N=""
|
||||
fi
|
||||
|
||||
OUTPUT="$OUTPUT$CSI$N$2"
|
||||
}
|
||||
|
||||
ansi::addColor() {
|
||||
OUTPUT="$OUTPUT$CSI${1}m"
|
||||
|
||||
if [ ! -z "$2" ]; then
|
||||
SUFFIX="$CSI${2}m$SUFFIX"
|
||||
fi
|
||||
}
|
||||
|
||||
ansi::colorTable() {
|
||||
local FNB_LOWER FNB_UPPER PADDED
|
||||
|
||||
FNB_LOWER="$(ansi::colorize 2 22 f)n$(ansi::colorize 1 22 b)"
|
||||
FNB_UPPER="$(ansi::colorize 2 22 F)N$(ansi::colorize 1 22 B)"
|
||||
printf 'bold %s ' "$(ansi::colorize 1 22 Sample)"
|
||||
printf 'faint %s ' "$(ansi::colorize 2 22 Sample)"
|
||||
printf 'italic %s\n' "$(ansi::colorize 3 23 Sample)"
|
||||
printf 'underline %s ' "$(ansi::colorize 4 24 Sample)"
|
||||
printf 'blink %s ' "$(ansi::colorize 5 25 Sample)"
|
||||
printf 'inverse %s\n' "$(ansi::colorize 7 27 Sample)"
|
||||
printf 'invisible %s\n' "$(ansi::colorize 8 28 Sample)"
|
||||
printf 'strike %s ' "$(ansi::colorize 9 29 Sample)"
|
||||
printf 'fraktur %s ' "$(ansi::colorize 20 23 Sample)"
|
||||
printf 'double-underline%s\n' "$(ansi::colorize 21 24 Sample)"
|
||||
printf 'frame %s ' "$(ansi::colorize 51 54 Sample)"
|
||||
printf 'encircle %s ' "$(ansi::colorize 52 54 Sample)"
|
||||
printf 'overline%s\n' "$(ansi::colorize 53 55 Sample)"
|
||||
printf '\n'
|
||||
printf ' black red green yellow blue magenta cyan white\n'
|
||||
for BG in 40:black 41:red 42:green 43:yellow 44:blue 45:magenta 46:cyan 47:white; do
|
||||
PADDED="bg-${BG:3} "
|
||||
PADDED="${PADDED:0:13}"
|
||||
printf '%s' "$PADDED"
|
||||
for FG in 30 31 32 33 34 35 36 37; do
|
||||
printf '%s%s;%sm' "$CSI" "${BG:0:2}" "${FG}"
|
||||
printf '%s' "$FNB_LOWER"
|
||||
printf '%s%sm' "$CSI" "$(( FG + 60 ))"
|
||||
printf '%s' "$FNB_UPPER"
|
||||
printf '%s0m ' "${CSI}"
|
||||
done
|
||||
printf '\n'
|
||||
printf ' +intense '
|
||||
for FG in 30 31 32 33 34 35 36 37; do
|
||||
printf '%s%s;%sm' "$CSI" "$(( ${BG:0:2} + 60 ))" "${FG}"
|
||||
printf '%s' "$FNB_LOWER"
|
||||
printf '%s%sm' "$CSI" "$(( FG + 60 ))"
|
||||
printf '%s' "$FNB_UPPER"
|
||||
printf '%s0m ' "${CSI}"
|
||||
done
|
||||
printf '\n'
|
||||
done
|
||||
printf '\n'
|
||||
printf 'Legend:\n'
|
||||
printf ' Normal color: f = faint, n = normal, b = bold.\n'
|
||||
printf ' Intense color: F = faint, N = normal, B = bold.\n'
|
||||
}
|
||||
|
||||
ansi::colorize() {
|
||||
printf '%s%sm%s%s%sm' "$CSI" "$1" "$3" "$CSI" "$2"
|
||||
}
|
||||
|
||||
ansi::isAnsiSupported() {
|
||||
# Idea: CSI c
|
||||
# Response = CSI ? 6 [234] ; 2 2 c
|
||||
# The "22" means ANSI color
|
||||
printf "can't tell yet\n"
|
||||
}
|
||||
|
||||
ansi::report() {
|
||||
local BUFF C
|
||||
|
||||
REPORT=""
|
||||
printf "%s%s" "$CSI" "$1"
|
||||
read -r -N ${#2} -s -t 1 BUFF
|
||||
|
||||
if [ "$BUFF" != "$2" ]; then
|
||||
return 1
|
||||
fi
|
||||
|
||||
read -r -N ${#3} -s -t 1 BUFF
|
||||
|
||||
while [ "$BUFF" != "$3" ]; do
|
||||
REPORT="$REPORT${BUFF:0:1}"
|
||||
read -r -N 1 -s -t 1 C || exit 1
|
||||
BUFF="${BUFF:1}$C"
|
||||
done
|
||||
}
|
||||
|
||||
ansi::showHelp() {
|
||||
cat <<EOF
|
||||
Generate ANSI escape codes
|
||||
|
||||
Please keep in mind that your terminal must support the code in order for you
|
||||
to see the effect properly.
|
||||
|
||||
Usage
|
||||
ansi [OPTIONS] [TEXT TO OUTPUT]
|
||||
|
||||
Option processing stops at the first unknown option or at "--". Options
|
||||
are applied in order as specified on the command line. Unless --no-restore
|
||||
is used, the options are unapplied in reverse order, restoring your
|
||||
terminal to normal.
|
||||
|
||||
Optional parameters are surrounded in brackets and use reasonable defaults.
|
||||
For instance, using --down will move the cursor down one line and --down=10
|
||||
moves the cursor down 10 lines.
|
||||
|
||||
Display Manipulation
|
||||
--insert-chars[=N], --ich[=N]
|
||||
Insert blanks at cursor, shifting the line right.
|
||||
--erase-display[=N], --ed[=N]
|
||||
Erase in display. 0=below, 1=above, 2=all,
|
||||
3=saved.
|
||||
--erase-line[=N], --el[=N]
|
||||
Erase in line. 0=right, 1=left, 2=all.
|
||||
--insert-lines[=N], --il[=N]
|
||||
--delete-lines[=N], --dl[=N]
|
||||
--delete-chars[=N], --dch[=N]
|
||||
--scroll-up[=N], --su[=N]
|
||||
--scroll-down[=N], --sd[=N]
|
||||
--erase-chars[=N], --ech[=N]
|
||||
--repeat[=N], --rep[=N] Repeat preceding character N times.
|
||||
|
||||
Cursor:
|
||||
--up[=N], --cuu[=N]
|
||||
--down[=N], --cud[=N]
|
||||
--forward[=N], --cuf[=N]
|
||||
--backward[=N], --cub[=N]
|
||||
--next-line[=N], --cnl[=N]
|
||||
--prev-line[=N], --cpl[=N]
|
||||
--column[=N], --cha[=N]
|
||||
--position[=[ROW],[COL]], --cup[=[ROW],[=COL]]
|
||||
--tab-forward[=N] Move forward N tab stops.
|
||||
--tab-backward[=N] Move backward N tab stops.
|
||||
--column-relative[=N], --hpr[=N]
|
||||
--line[=N], --vpa[=N]
|
||||
--line-relative[=N], --vpr[=N]
|
||||
--save-cursor Saves the cursor position. Restores the cursor
|
||||
after writing text to the terminal unless
|
||||
--no-restore is also used.
|
||||
--restore-cursor Just restores the cursor.
|
||||
--hide-cursor Will automatically show cursor unless --no-restore
|
||||
is also used.
|
||||
--show-cursor
|
||||
|
||||
Colors:
|
||||
Attributes:
|
||||
--bold, --faint, --italic, --underline, --blink, --inverse,
|
||||
--invisible, --strike, --fraktur, --double-underline, --frame,
|
||||
--encircle, --overline
|
||||
Foreground:
|
||||
--black, --red, --green, --yellow, --blue, --magenta, --cyan, --white,
|
||||
--black-intense, --red-intense, --green-intense, --yellow-intense,
|
||||
--blue-intense, --magenta-intense, --cyan-intense, --white-intense
|
||||
Background:
|
||||
--bg-black, --bg-red, --bg-green, --bg-yellow, --bg-blue,
|
||||
--bg-magenta, --bg-cyan, --bg-white, --bg-black-intense,
|
||||
--bg-red-intense, --bg-green-intense, --bg-yellow-intense,
|
||||
--bg-blue-intense, --bg-magenta-intense, --bg-cyan-intense,
|
||||
--bg-white-intense
|
||||
Reset:
|
||||
--reset-attrib Removes bold, italics, etc.
|
||||
--reset-foreground Sets foreground to default color.
|
||||
--reset-background Sets background to default color.
|
||||
--reset-color Resets attributes, foreground, background.
|
||||
|
||||
Report:
|
||||
** NOTE: These require reading from stdin. Results are sent to stdout.
|
||||
** If no response from terminal in 1 second, these commands fail.
|
||||
--report-position ROW,COL
|
||||
--report-window-state "open" or "iconified"
|
||||
--report-window-position X,Y
|
||||
--report-window-pixels HEIGHT,WIDTH
|
||||
--report-window-chars ROWS,COLS
|
||||
--report-screen-chars ROWS,COLS of the entire screen
|
||||
--report-icon
|
||||
--report-title
|
||||
|
||||
Miscellaneous:
|
||||
--color-table Display a color table.
|
||||
--icon=NAME Set the terminal's icon name.
|
||||
--title=TITLE Set the terminal's window title.
|
||||
--no-restore Do not issue reset codes when changing colors.
|
||||
For example, if you change the color to green,
|
||||
normally the color is restored to default
|
||||
afterwards. With this flag, the color will
|
||||
stay green even when the command finishes.
|
||||
-n, --newline Add a newline at the end.
|
||||
--escape Allow text passed in to contain escape sequences.
|
||||
--bell Add the terminal's bell sequence to the output.
|
||||
--reset Reset colors, clear screen, show cursor, move
|
||||
cursor to 1,1.
|
||||
EOF
|
||||
}
|
||||
|
||||
ansi() {
|
||||
# Handle long options until we hit an unrecognized thing
|
||||
local CONTINUE=true
|
||||
local RESTORE=true
|
||||
local NEWLINE=false
|
||||
local ESCAPE=false
|
||||
local ESC=$'\033'
|
||||
local CSI="${ESC}["
|
||||
local OSC="${ESC}]"
|
||||
local ST="${ESC}\\"
|
||||
local OUTPUT=""
|
||||
local SUFFIX=""
|
||||
local BELL=$'\007'
|
||||
|
||||
while $CONTINUE; do
|
||||
case "$1" in
|
||||
--help | -h | -\?)
|
||||
ansi::showHelp
|
||||
;;
|
||||
|
||||
# Display Manipulation
|
||||
--insert-chars | --insert-chars=* | --ich | --ich=*)
|
||||
ansi::addCode "$1" @
|
||||
;;
|
||||
|
||||
--erase-display | --erase-display=* | --ed | --ed=*)
|
||||
ansi::addCode "$1" J
|
||||
;;
|
||||
|
||||
--erase-line | --erase-line=* | --el | --el=*)
|
||||
ansi::addCode "$1" K
|
||||
;;
|
||||
|
||||
--insert-lines | --insert-lines=* | --il | --il=*)
|
||||
ansi::addCode "$1" L
|
||||
;;
|
||||
|
||||
--delete-lines | --delete-lines=* | --dl | --dl=*)
|
||||
ansi::addCode "$1" M
|
||||
;;
|
||||
|
||||
--delete-chars | --delete-chars=* | --dch | --dch=*)
|
||||
ansi::addCode "$1" P
|
||||
;;
|
||||
|
||||
--scroll-up | --scroll-up=* | --su | --su=*)
|
||||
ansi::addCode "$1" S
|
||||
;;
|
||||
|
||||
--scroll-down | --scroll-down=* | --sd | --sd=*)
|
||||
ansi::addCode "$1" T
|
||||
;;
|
||||
|
||||
--erase-chars | --erase-chars=* | --ech | --ech=*)
|
||||
ansi::addCode "$1" X
|
||||
;;
|
||||
|
||||
--repeat | --repeat=* | --rep | --rep=N)
|
||||
ansi::addCode "$1" b
|
||||
;;
|
||||
|
||||
# Cursor Positioning
|
||||
--up | --up=* | --cuu | --cuu=*)
|
||||
ansi::addCode "$1" A
|
||||
;;
|
||||
|
||||
--down | --down=* | --cud | --cud=*)
|
||||
ansi::addCode "$1" B
|
||||
;;
|
||||
|
||||
--forward | --forward=* | --cuf | --cuf=*)
|
||||
ansi::addCode "$1" C
|
||||
;;
|
||||
|
||||
--backward | --backward=*| --cub | --cub=*)
|
||||
ansi::addCode "$1" D
|
||||
;;
|
||||
|
||||
--next-line | --next-line=* | --cnl | --cnl=*)
|
||||
ansi::addCode "$1" E
|
||||
;;
|
||||
|
||||
--prev-line | --prev-line=* | --cpl | --cpl=*)
|
||||
ansi::addCode "$1" F
|
||||
;;
|
||||
|
||||
--column | --column=* | --cha | --cha=*)
|
||||
ansi::addCode "$1" G
|
||||
;;
|
||||
|
||||
--position | --position=* | --cup | --cup=*)
|
||||
ansi::addCode "$1" H
|
||||
;;
|
||||
|
||||
--tab-forward | --tab-forward=* | --cht | --cht=*)
|
||||
ansi::addCode "$1" I
|
||||
;;
|
||||
|
||||
--tab-backward | --tab-backward=* | --cbt | --cbt=*)
|
||||
ansi::addCode "$1" Z
|
||||
;;
|
||||
|
||||
--column-relative | --column-relative=* | --hpr | --hpr=*)
|
||||
ansi::addCode "$1" 'a'
|
||||
;;
|
||||
|
||||
--line | --line=* | --vpa | --vpa=*)
|
||||
ansi::addCode "$1" 'd'
|
||||
;;
|
||||
|
||||
--line-relative | --line-relative=* | --vpr | --vpr=*)
|
||||
ansi::addCode "$1" 'e'
|
||||
;;
|
||||
|
||||
--save-cursor)
|
||||
OUTPUT="$OUTPUT${CSI}s"
|
||||
SUFFIX="${CSI}u$SUFFIX"
|
||||
;;
|
||||
|
||||
--restore-cursor)
|
||||
OUTPUT="$OUTPUT${CSI}u"
|
||||
;;
|
||||
|
||||
--hide-cursor)
|
||||
OUTPUT="$OUTPUT${CSI}?25l"
|
||||
SUFFIX="${CSI}?25h"
|
||||
;;
|
||||
|
||||
--show-cursor)
|
||||
OUTPUT="$OUTPUT${CSI}?25h"
|
||||
;;
|
||||
|
||||
# Colors - Attributes
|
||||
--bold)
|
||||
ansi::addColor 1 22
|
||||
;;
|
||||
|
||||
--faint)
|
||||
ansi::addColor 2 22
|
||||
;;
|
||||
|
||||
--italic)
|
||||
ansi::addColor 3 23
|
||||
;;
|
||||
|
||||
--underline)
|
||||
ansi::addColor 4 24
|
||||
;;
|
||||
|
||||
--blink)
|
||||
ansi::addColor 5 25
|
||||
;;
|
||||
|
||||
--inverse)
|
||||
ansi::addColor 7 27
|
||||
;;
|
||||
|
||||
--invisible)
|
||||
ansi::addColor 8 28
|
||||
;;
|
||||
|
||||
--strike)
|
||||
ansi::addColor 9 20
|
||||
;;
|
||||
|
||||
--fraktur)
|
||||
ansi::addColor 20 23
|
||||
;;
|
||||
|
||||
--double-underline)
|
||||
ansi::addColor 21 24
|
||||
;;
|
||||
|
||||
--frame)
|
||||
ansi::addColor 51 54
|
||||
;;
|
||||
|
||||
--encircle)
|
||||
ansi::addColor 52 54
|
||||
;;
|
||||
|
||||
--overline)
|
||||
ansi::addColor 53 55
|
||||
;;
|
||||
|
||||
# Colors - Foreground
|
||||
--black)
|
||||
ansi::addColor 30 39
|
||||
;;
|
||||
|
||||
--red)
|
||||
ansi::addColor 31 39
|
||||
;;
|
||||
|
||||
--green)
|
||||
ansi::addColor 32 39
|
||||
;;
|
||||
|
||||
--yellow)
|
||||
ansi::addColor 33 39
|
||||
;;
|
||||
|
||||
--blue)
|
||||
ansi::addColor 34 39
|
||||
;;
|
||||
|
||||
--magenta)
|
||||
ansi::addColor 35 39
|
||||
;;
|
||||
|
||||
--cyan)
|
||||
ansi::addColor 36 39
|
||||
;;
|
||||
|
||||
--white)
|
||||
ansi::addColor 37 39
|
||||
;;
|
||||
|
||||
--black-intense)
|
||||
ansi::addColor 90 39
|
||||
;;
|
||||
|
||||
--red-intense)
|
||||
ansi::addColor 91 39
|
||||
;;
|
||||
|
||||
--green-intense)
|
||||
ansi::addColor 92 39
|
||||
;;
|
||||
|
||||
--yellow-intense)
|
||||
ansi::addColor 93 39
|
||||
;;
|
||||
|
||||
--blue-intense)
|
||||
ansi::addColor 94 39
|
||||
;;
|
||||
|
||||
--magenta-intense)
|
||||
ansi::addColor 95 39
|
||||
;;
|
||||
|
||||
--cyan-intense)
|
||||
ansi::addColor 96 39
|
||||
;;
|
||||
|
||||
--white-intense)
|
||||
ansi::addColor 97 39
|
||||
;;
|
||||
|
||||
# Colors - Background
|
||||
--bg-black)
|
||||
ansi::addColor 40 49
|
||||
;;
|
||||
|
||||
--bg-red)
|
||||
ansi::addColor 41 49
|
||||
;;
|
||||
|
||||
--bg-green)
|
||||
ansi::addColor 42 49
|
||||
;;
|
||||
|
||||
--bg-yellow)
|
||||
ansi::addColor 43 49
|
||||
;;
|
||||
|
||||
--bg-blue)
|
||||
ansi::addColor 44 49
|
||||
;;
|
||||
|
||||
--bg-magenta)
|
||||
ansi::addColor 45 49
|
||||
;;
|
||||
|
||||
--bg-cyan)
|
||||
ansi::addColor 46 49
|
||||
;;
|
||||
|
||||
--bg-white)
|
||||
ansi::addColor 47 49
|
||||
;;
|
||||
|
||||
--bg-black-intense)
|
||||
ansi::addColor 100 49
|
||||
;;
|
||||
|
||||
--bg-red-intense)
|
||||
ansi::addColor 101 49
|
||||
;;
|
||||
|
||||
--bg-green-intense)
|
||||
ansi::addColor 102 49
|
||||
;;
|
||||
|
||||
--bg-yellow-intense)
|
||||
ansi::addColor 103 49
|
||||
;;
|
||||
|
||||
--bg-blue-intense)
|
||||
ansi::addColor 104 49
|
||||
;;
|
||||
|
||||
--bg-magenta-intense)
|
||||
ansi::addColor 105 49
|
||||
;;
|
||||
|
||||
--bg-cyan-intense)
|
||||
ansi::addColor 106 49
|
||||
;;
|
||||
|
||||
--bg-white-intense)
|
||||
ansi::addColor 107 49
|
||||
;;
|
||||
|
||||
# Colors - Reset
|
||||
--reset-attrib)
|
||||
OUTPUT="$OUTPUT${CSI}22;23;24;25;27;28;29;54;55m"
|
||||
;;
|
||||
|
||||
--reset-foreground)
|
||||
OUTPUT="$OUTPUT${CSI}39m"
|
||||
;;
|
||||
|
||||
--reset-background)
|
||||
OUTPUT="$OUTPUT${CSI}39m"
|
||||
;;
|
||||
|
||||
--reset-color)
|
||||
OUTPUT="$OUTPUT${CSI}0m"
|
||||
;;
|
||||
|
||||
# Reporting
|
||||
--report-position)
|
||||
ansi::report 6n "$CSI" R || exit 1
|
||||
printf '%s\n' "${REPORT//;/,}"
|
||||
;;
|
||||
|
||||
--report-window-state)
|
||||
ansi::report 11t "$CSI" t || exit 1
|
||||
case "$REPORT" in
|
||||
1)
|
||||
printf 'open\n'
|
||||
;;
|
||||
|
||||
2)
|
||||
printf 'iconified\n'
|
||||
;;
|
||||
|
||||
*)
|
||||
printf 'unknown (%s)\n' "$REPORT"
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
--report-window-position)
|
||||
ansi::report 13t "${CSI}3;" t || exit 1
|
||||
printf '%s\n' "${REPORT//;/,}"
|
||||
;;
|
||||
|
||||
--report-window-pixels)
|
||||
ansi::report 14t "${CSI}4;" t || exit 1
|
||||
printf '%s\n' "${REPORT//;/,}"
|
||||
;;
|
||||
|
||||
--report-window-chars)
|
||||
ansi::report 18t "${CSI}8;" t || exit 1
|
||||
printf '%s\n' "${REPORT//;/,}"
|
||||
;;
|
||||
|
||||
--report-screen-chars)
|
||||
ansi::report 19t "${CSI}9;" t || exit 1
|
||||
printf '%s\n' "${REPORT//;/,}"
|
||||
;;
|
||||
|
||||
--report-icon)
|
||||
ansi::report 20t "${OSC}L" "$ST" || exit 1
|
||||
printf '%s\n' "$REPORT"
|
||||
;;
|
||||
|
||||
--report-title)
|
||||
ansi::report 21t "${OSC}l" "$ST" || exit 1
|
||||
printf '%s\n' "$REPORT"
|
||||
;;
|
||||
|
||||
# Miscellaneous
|
||||
--color-table)
|
||||
ansi::colorTable
|
||||
;;
|
||||
|
||||
--icon=*)
|
||||
OUTPUT="$OUTPUT${OSC}1;${1#*=}$ST"
|
||||
;;
|
||||
|
||||
--title=*)
|
||||
OUTPUT="$OUTPUT${OSC}2;${1#*=}$ST"
|
||||
;;
|
||||
|
||||
--no-restore)
|
||||
RESTORE=false
|
||||
;;
|
||||
|
||||
-n | --newline)
|
||||
NEWLINE=true
|
||||
;;
|
||||
|
||||
--escape)
|
||||
ESCAPE=true
|
||||
;;
|
||||
|
||||
--bell)
|
||||
OUTPUT="$OUTPUT$BELL"
|
||||
;;
|
||||
|
||||
--reset)
|
||||
# 0m - reset all colors and attributes
|
||||
# 2J - clear terminal
|
||||
# 1;1H - move to 1,1
|
||||
# ?25h - show cursor
|
||||
OUTPUT="$OUTPUT${CSI}0m${CSI}2J${CSI}1;1H${CSI}?25h"
|
||||
;;
|
||||
|
||||
--)
|
||||
CONTINUE=false
|
||||
shift
|
||||
;;
|
||||
|
||||
*)
|
||||
CONTINUE=false
|
||||
;;
|
||||
esac
|
||||
|
||||
if $CONTINUE; then
|
||||
shift
|
||||
fi
|
||||
done
|
||||
|
||||
printf '%s' "$OUTPUT"
|
||||
|
||||
if $ESCAPE; then
|
||||
printf '%s' "${1+"$@"}"
|
||||
else
|
||||
printf '%s' "${1+"$@"}"
|
||||
fi
|
||||
|
||||
if $RESTORE; then
|
||||
printf '%s' "$SUFFIX"
|
||||
fi
|
||||
|
||||
if $NEWLINE; then
|
||||
printf '\n'
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Run if not sourced
|
||||
if [[ "$0" == "${BASH_SOURCE[0]}" ]]; then
|
||||
ansi "$@"
|
||||
fi
|
||||
|
334
bin/desk
Executable file
334
bin/desk
Executable file
|
@ -0,0 +1,334 @@
|
|||
#!/usr/bin/env bash
|
||||
# vim: set filetype=sh:
|
||||
|
||||
PREFIX="${DESK_DIR:-$HOME/.desk}"
|
||||
DESKS="${DESK_DESKS_DIR:-$PREFIX/desks}"
|
||||
DESKFILE_NAME=Deskfile
|
||||
|
||||
|
||||
## Commands
|
||||
|
||||
cmd_version() {
|
||||
echo "◲ desk 0.6.0"
|
||||
}
|
||||
|
||||
|
||||
cmd_usage() {
|
||||
cmd_version
|
||||
echo
|
||||
cat <<_EOF
|
||||
Usage:
|
||||
|
||||
$PROGRAM
|
||||
List the current desk and any associated aliases. If no desk
|
||||
is being used, display available desks.
|
||||
$PROGRAM init
|
||||
Initialize desk configuration.
|
||||
$PROGRAM (list|ls)
|
||||
List all desks along with a description.
|
||||
$PROGRAM (.|go) [<desk-name-or-path> [shell-args...]]
|
||||
Activate a desk. Extra arguments are passed onto shell. If called with
|
||||
no arguments, look for a Deskfile in the current directory. If not a
|
||||
recognized desk, try as a path to directory containing a Deskfile.
|
||||
$PROGRAM run <desk-name> <cmd>
|
||||
Run a command within a desk's environment then exit. Think '\$SHELL -c'.
|
||||
$PROGRAM edit [desk-name]
|
||||
Edit (or create) a deskfile with the name specified, otherwise
|
||||
edit the active deskfile.
|
||||
$PROGRAM help
|
||||
Show this text.
|
||||
$PROGRAM version
|
||||
Show version information.
|
||||
|
||||
Since desk spawns a shell, to deactivate and "pop" out a desk, you
|
||||
simply need to exit or otherwise end the current shell process.
|
||||
_EOF
|
||||
}
|
||||
|
||||
cmd_init() {
|
||||
if [ -d "$PREFIX" ]; then
|
||||
echo "Desk dir already exists at ${PREFIX}"
|
||||
exit 1
|
||||
fi
|
||||
read -p "Where do you want to store your deskfiles? (default: ${PREFIX}): " \
|
||||
NEW_PREFIX
|
||||
[ -z "${NEW_PREFIX}" ] && NEW_PREFIX="$PREFIX"
|
||||
|
||||
if [ ! -d "${NEW_PREFIX}" ]; then
|
||||
echo "${NEW_PREFIX} doesn't exist, attempting to create."
|
||||
mkdir -p "$NEW_PREFIX/desks"
|
||||
fi
|
||||
|
||||
local SHELLTYPE=$(get_running_shell)
|
||||
|
||||
case "${SHELLTYPE}" in
|
||||
bash) local SHELLRC="${HOME}/.bashrc" ;;
|
||||
fish) local SHELLRC="${HOME}/.config/fish/config.fish" ;;
|
||||
zsh) local SHELLRC="${HOME}/.zshrc" ;;
|
||||
esac
|
||||
|
||||
read -p "Where's your shell rc file? (default: ${SHELLRC}): " \
|
||||
USER_SHELLRC
|
||||
[ -z "${USER_SHELLRC}" ] && USER_SHELLRC="$SHELLRC"
|
||||
if [ ! -f "$USER_SHELLRC" ]; then
|
||||
echo "${USER_SHELLRC} doesn't exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf "\n# Hook for desk activation\n" >> "$USER_SHELLRC"
|
||||
|
||||
# Since the hook is appended to the rc file, its exit status becomes
|
||||
# the exit status of `source $USER_SHELLRC` which typically
|
||||
# indicates if something went wrong. If $DESK_ENV is void, `test`
|
||||
# sets exit status to 1. That, however, is part of desk's normal
|
||||
# operation, so we clear exit status after that.
|
||||
if [ "$SHELLTYPE" == "fish" ]; then
|
||||
echo "test -n \"\$DESK_ENV\"; and . \"\$DESK_ENV\"; or true" >> "$USER_SHELLRC"
|
||||
else
|
||||
echo "[ -n \"\$DESK_ENV\" ] && source \"\$DESK_ENV\" || true" >> "$USER_SHELLRC"
|
||||
fi
|
||||
|
||||
echo "Done. Start adding desks to ${NEW_PREFIX}/desks!"
|
||||
}
|
||||
|
||||
|
||||
cmd_go() {
|
||||
# TODESK ($1) may either be
|
||||
#
|
||||
# - the name of a desk in $DESKS/
|
||||
# - a path to a Deskfile
|
||||
# - a directory containing a Deskfile
|
||||
# - empty -> `./Deskfile`
|
||||
#
|
||||
local TODESK="$1"
|
||||
local DESKEXT=$(get_deskfile_extension)
|
||||
local DESKPATH="$(find "${DESKS}/" -name "${TODESK}${DESKEXT}" 2>/dev/null)"
|
||||
|
||||
local POSSIBLE_DESKFILE_DIR="${TODESK%$DESKFILE_NAME}"
|
||||
if [ -z "$POSSIBLE_DESKFILE_DIR" ]; then
|
||||
POSSIBLE_DESKFILE_DIR="."
|
||||
fi
|
||||
|
||||
# If nothing could be found in $DESKS/, check to see if this is a path to
|
||||
# a Deskfile.
|
||||
if [[ -z "$DESKPATH" && -d "$POSSIBLE_DESKFILE_DIR" ]]; then
|
||||
if [ ! -f "${POSSIBLE_DESKFILE_DIR}/${DESKFILE_NAME}" ]; then
|
||||
echo "No Deskfile found in '${POSSIBLE_DESKFILE_DIR}'"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local REALPATH=$( cd $POSSIBLE_DESKFILE_DIR && pwd )
|
||||
DESKPATH="${REALPATH}/${DESKFILE_NAME}"
|
||||
TODESK=$(basename "$REALPATH")
|
||||
fi
|
||||
|
||||
# Shift desk name so we can forward on all arguments to the shell.
|
||||
shift;
|
||||
|
||||
if [ -z "$DESKPATH" ]; then
|
||||
echo "Desk $TODESK (${TODESK}${DESKEXT}) not found in $DESKS"
|
||||
exit 1
|
||||
else
|
||||
local SHELL_EXEC="$(get_running_shell)"
|
||||
DESK_NAME="${TODESK}" DESK_ENV="${DESKPATH}" exec "${SHELL_EXEC}" "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
cmd_run() {
|
||||
local TODESK="$1"
|
||||
shift;
|
||||
cmd_go "$TODESK" -ic "$@"
|
||||
}
|
||||
|
||||
|
||||
# Usage: desk list [options]
|
||||
# Description: List all desks along with a description
|
||||
# --only-names: List only the names of the desks
|
||||
# --no-format: Use ' - ' to separate names from descriptions
|
||||
cmd_list() {
|
||||
if [ ! -d "${DESKS}/" ]; then
|
||||
echo "No desk dir! Run 'desk init'."
|
||||
exit 1
|
||||
fi
|
||||
local SHOW_DESCRIPTIONS DESKEXT AUTO_ALIGN name desc len longest out
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--only-names) SHOW_DESCRIPTIONS=false && AUTO_ALIGN=false ;;
|
||||
--no-format) AUTO_ALIGN=false ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
DESKEXT=$(get_deskfile_extension)
|
||||
out=""
|
||||
longest=0
|
||||
|
||||
while read -d '' -r f; do
|
||||
name=$(basename "${f/${DESKEXT}//}")
|
||||
if [[ "$SHOW_DESCRIPTIONS" = false ]]; then
|
||||
out+="$name"$'\n'
|
||||
else
|
||||
desc=$(echo_description "$f")
|
||||
out+="$name - $desc"$'\n'
|
||||
len=${#name}
|
||||
(( len > longest )) && longest=$len
|
||||
fi
|
||||
done < <(find "${DESKS}/" -name "*${DESKEXT}" -print0)
|
||||
if [[ "$AUTO_ALIGN" != false ]]; then
|
||||
print_aligned "$out" "$longest"
|
||||
else
|
||||
printf "%s" "$out"
|
||||
fi
|
||||
}
|
||||
|
||||
# Usage: desk [options]
|
||||
# Description: List the current desk and any associated aliases. If no desk is being used, display available desks
|
||||
# --no-format: Use ' - ' to separate alias/export/function names from their descriptions
|
||||
cmd_current() {
|
||||
if [ -z "$DESK_ENV" ]; then
|
||||
printf "No desk activated.\n\n%s" "$(cmd_list)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local DESKPATH=$DESK_ENV
|
||||
local CALLABLES=$(get_callables "$DESKPATH")
|
||||
local AUTO_ALIGN len longest out
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--no-format) AUTO_ALIGN=false ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
printf "%s - %s\n" "$DESK_NAME" "$(echo_description "$DESKPATH")"
|
||||
|
||||
if [[ -n "$CALLABLES" ]]; then
|
||||
|
||||
longest=0
|
||||
out=$'\n'
|
||||
for NAME in $CALLABLES; do
|
||||
# Last clause in the grep regexp accounts for fish functions.
|
||||
len=$((${#NAME} + 2))
|
||||
(( len > longest )) && longest=$len
|
||||
local DOCLINE=$(
|
||||
grep -B 1 -E \
|
||||
"^(alias ${NAME}=|export ${NAME}=|(function )?${NAME}( )?\()|function $NAME" "$DESKPATH" \
|
||||
| grep "#")
|
||||
|
||||
if [ -z "$DOCLINE" ]; then
|
||||
out+=" ${NAME}"$'\n'
|
||||
else
|
||||
out+=" ${NAME} - ${DOCLINE##\# }"$'\n'
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$AUTO_ALIGN" != false ]]; then
|
||||
print_aligned "$out" "$longest"
|
||||
else
|
||||
printf "%s" "$out"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
cmd_edit() {
|
||||
if [ $# -eq 0 ]; then
|
||||
if [ "$DESK_NAME" == "" ]; then
|
||||
echo "No desk activated."
|
||||
exit 3
|
||||
fi
|
||||
local DESKNAME_TO_EDIT="$DESK_NAME"
|
||||
elif [ $# -eq 1 ]; then
|
||||
local DESKNAME_TO_EDIT="$1"
|
||||
else
|
||||
echo "Usage: ${PROGRAM} edit [desk-name]"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
local DESKEXT=$(get_deskfile_extension)
|
||||
local EDIT_PATH="${DESKS}/${DESKNAME_TO_EDIT}${DESKEXT}"
|
||||
|
||||
${EDITOR:-vi} "$EDIT_PATH"
|
||||
}
|
||||
|
||||
## Utilities
|
||||
|
||||
FNAME_CHARS='[a-zA-Z0-9_-]'
|
||||
|
||||
# Echo the description of a desk. $1 is the deskfile.
|
||||
echo_description() {
|
||||
local descline=$(grep -E "#\s+Description" "$1")
|
||||
echo "${descline##*Description: }"
|
||||
}
|
||||
|
||||
# Echo a list of aliases, exports, and functions for a given desk
|
||||
get_callables() {
|
||||
local DESKPATH=$1
|
||||
grep -E "^(alias |export |(function )?${FNAME_CHARS}+ ?\()|function $NAME" "$DESKPATH" \
|
||||
| sed 's/alias \([^= ]*\)=.*/\1/' \
|
||||
| sed 's/export \([^= ]*\)=.*/\1/' \
|
||||
| sed -E "s/(function )?(${FNAME_CHARS}+) ?\(\).*/\2/" \
|
||||
| sed -E "s/function (${FNAME_CHARS}+).*/\1/"
|
||||
}
|
||||
|
||||
get_running_shell() {
|
||||
# Echo the name of the parent shell via procfs, if we have it available.
|
||||
# Otherwise, try to use ps with bash's parent pid.
|
||||
if [ -e /proc ]; then
|
||||
# Get cmdline procfile of the process running this script.
|
||||
local CMDLINE_FILE="/proc/$(grep PPid /proc/$$/status | cut -f2)/cmdline"
|
||||
|
||||
if [ -f "$CMDLINE_FILE" ]; then
|
||||
# Strip out any verion that may be attached to the shell executable.
|
||||
# Strip leading dash for login shells.
|
||||
local CMDLINE_SHELL=$(sed -r -e 's/\x0.*//' -e 's/^-//' "$CMDLINE_FILE")
|
||||
fi
|
||||
else
|
||||
# Strip leading dash for login shells.
|
||||
# If the parent process is a login shell, guess bash.
|
||||
local CMDLINE_SHELL=$(ps -o args -p $PPID | tail -1 | sed -e 's/login/bash/' -e 's/^-//')
|
||||
fi
|
||||
|
||||
if [ ! -z "$CMDLINE_SHELL" ]; then
|
||||
basename "$CMDLINE_SHELL"
|
||||
exit
|
||||
fi
|
||||
|
||||
# Fall back to $SHELL otherwise.
|
||||
basename "$SHELL"
|
||||
exit
|
||||
}
|
||||
|
||||
# Echo the extension of recognized deskfiles (.fish for fish)
|
||||
get_deskfile_extension() {
|
||||
if [ "$(get_running_shell)" == "fish" ]; then
|
||||
echo '.fish'
|
||||
else
|
||||
echo '.sh'
|
||||
fi
|
||||
}
|
||||
|
||||
# Echo first argument as key/value pairs aligned into two columns; second argument is the longest key
|
||||
print_aligned() {
|
||||
local out=$1 longest=$2
|
||||
printf "%s" "$out" | awk -v padding="$longest" -F' - ' '{
|
||||
printf "%-*s %s\n", padding, $1, substr($0, index($0, " - ")+2, length($0))
|
||||
}'
|
||||
}
|
||||
|
||||
|
||||
PROGRAM="${0##*/}"
|
||||
|
||||
case "$1" in
|
||||
init) shift; cmd_init "$@" ;;
|
||||
help|--help) shift; cmd_usage "$@" ;;
|
||||
version|--version) shift; cmd_version "$@" ;;
|
||||
ls|list) shift; cmd_list "$@" ;;
|
||||
go|.) shift; cmd_go "$@" ;;
|
||||
run) shift; cmd_run "$@" ;;
|
||||
edit) shift; cmd_edit "$@" ;;
|
||||
*) cmd_current "$@" ;;
|
||||
esac
|
||||
exit 0
|
||||
|
128
bin/is
Executable file
128
bin/is
Executable file
|
@ -0,0 +1,128 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2016 Józef Sokołowski
|
||||
# Distributed under the MIT License
|
||||
#
|
||||
# For most current version checkout repository:
|
||||
# https://github.com/qzb/is.sh
|
||||
#
|
||||
|
||||
is() {
|
||||
if [ "$1" == "--help" ]; then
|
||||
cat << EOF
|
||||
Conditions:
|
||||
is equal VALUE_A VALUE_B
|
||||
is matching REGEXP VALUE
|
||||
is substring VALUE_A VALUE_B
|
||||
is empty VALUE
|
||||
is number VALUE
|
||||
is gt NUMBER_A NUMBER_B
|
||||
is lt NUMBER_A NUMBER_B
|
||||
is ge NUMBER_A NUMBER_B
|
||||
is le NUMBER_A NUMBER_B
|
||||
is file PATH
|
||||
is dir PATH
|
||||
is link PATH
|
||||
is existing PATH
|
||||
is readable PATH
|
||||
is writeable PATH
|
||||
is executable PATH
|
||||
is available COMMAND
|
||||
is older PATH_A PATH_B
|
||||
is newer PATH_A PATH_B
|
||||
is true VALUE
|
||||
is false VALUE
|
||||
|
||||
Negation:
|
||||
is not equal VALUE_A VALUE_B
|
||||
EOF
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ "$1" == "--version" ]; then
|
||||
echo "is.sh 1.1.0"
|
||||
exit
|
||||
fi
|
||||
|
||||
local condition="$1"
|
||||
local value_a="$2"
|
||||
local value_b="$3"
|
||||
|
||||
if [ "$condition" == "not" ]; then
|
||||
shift 1
|
||||
! is "${@}"
|
||||
return $?
|
||||
fi
|
||||
|
||||
if [ "$condition" == "a" ] || [ "$condition" == "an" ] || [ "$condition" == "the" ]; then
|
||||
shift 1
|
||||
is "${@}"
|
||||
return $?
|
||||
fi
|
||||
|
||||
case "$condition" in
|
||||
file)
|
||||
[ -f "$value_a" ]; return $?;;
|
||||
dir|directory)
|
||||
[ -d "$value_a" ]; return $?;;
|
||||
link|symlink)
|
||||
[ -L "$value_a" ]; return $?;;
|
||||
existent|existing|exist|exists)
|
||||
[ -e "$value_a" ]; return $?;;
|
||||
readable)
|
||||
[ -r "$value_a" ]; return $?;;
|
||||
writeable)
|
||||
[ -w "$value_a" ]; return $?;;
|
||||
executable)
|
||||
[ -x "$value_a" ]; return $?;;
|
||||
available|installed)
|
||||
which "$value_a"; return $?;;
|
||||
empty)
|
||||
[ -z "$value_a" ]; return $?;;
|
||||
number)
|
||||
echo "$value_a" | grep -E '^[0-9]+(\.[0-9]+)?$'; return $?;;
|
||||
older)
|
||||
[ "$value_a" -ot "$value_b" ]; return $?;;
|
||||
newer)
|
||||
[ "$value_a" -nt "$value_b" ]; return $?;;
|
||||
gt)
|
||||
is not a number "$value_a" && return 1;
|
||||
is not a number "$value_b" && return 1;
|
||||
awk "BEGIN {exit $value_a > $value_b ? 0 : 1}"; return $?;;
|
||||
lt)
|
||||
is not a number "$value_a" && return 1;
|
||||
is not a number "$value_b" && return 1;
|
||||
awk "BEGIN {exit $value_a < $value_b ? 0 : 1}"; return $?;;
|
||||
ge)
|
||||
is not a number "$value_a" && return 1;
|
||||
is not a number "$value_b" && return 1;
|
||||
awk "BEGIN {exit $value_a >= $value_b ? 0 : 1}"; return $?;;
|
||||
le)
|
||||
is not a number "$value_a" && return 1;
|
||||
is not a number "$value_b" && return 1;
|
||||
awk "BEGIN {exit $value_a <= $value_b ? 0 : 1}"; return $?;;
|
||||
eq|equal)
|
||||
[ "$value_a" = "$value_b" ] && return 0;
|
||||
is not a number "$value_a" && return 1;
|
||||
is not a number "$value_b" && return 1;
|
||||
awk "BEGIN {exit $value_a == $value_b ? 0 : 1}"; return $?;;
|
||||
match|matching)
|
||||
echo "$value_b" | grep -xE "$value_a"; return $?;;
|
||||
substr|substring)
|
||||
echo "$value_b" | grep -F "$value_a"; return $?;;
|
||||
true)
|
||||
[ "$value_a" == true ] || [ "$value_a" == 0 ]; return $?;;
|
||||
false)
|
||||
[ "$value_a" != true ] && [ "$value_a" != 0 ]; return $?;;
|
||||
esac > /dev/null
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
if is not equal "${BASH_SOURCE[0]}" "$0"; then
|
||||
export -f is
|
||||
else
|
||||
is "${@}"
|
||||
exit $?
|
||||
fi
|
||||
|
156
bin/lj
Executable file
156
bin/lj
Executable file
|
@ -0,0 +1,156 @@
|
|||
#!/usr/bin/env zsh
|
||||
|
||||
# Create a mapping of log levels to their names
|
||||
typeset -A _log_levels
|
||||
_log_levels=(
|
||||
'emergency' 0
|
||||
'alert' 1
|
||||
'critical' 2
|
||||
'error' 3
|
||||
'warning' 4
|
||||
'notice' 5
|
||||
'info' 6
|
||||
'debug' 7
|
||||
)
|
||||
|
||||
###
|
||||
# Output usage information and exit
|
||||
###
|
||||
function _lumberjack_usage() {
|
||||
echo "\033[0;33mUsage:\033[0;m"
|
||||
echo " lj [options] [<level>] <message>"
|
||||
echo
|
||||
echo "\033[0;33mOptions:\033[0;m"
|
||||
echo " -h, --help Output help text and exit"
|
||||
echo " -v, --version Output version information and exit"
|
||||
echo " -f, --file Set the logfile and exit"
|
||||
echo " -l, --level Set the log level and exit"
|
||||
echo
|
||||
echo "\033[0;33mLevels:\033[0;m"
|
||||
echo " emergency"
|
||||
echo " alert"
|
||||
echo " critical"
|
||||
echo " error"
|
||||
echo " warning"
|
||||
echo " notice"
|
||||
echo " info"
|
||||
echo " debug"
|
||||
}
|
||||
|
||||
###
|
||||
# Output the message to the logfile
|
||||
###
|
||||
function _lumberjack_message() {
|
||||
local level="$1" file="$2" logtype="$3" msg="${(@)@:4}"
|
||||
|
||||
# If the file string is empty, output an error message
|
||||
if [[ -z $file ]]; then
|
||||
echo "\033[0;31mNo logfile has been set for this process. Use \`lumberjack --file /path/to/file\` to set it\033[0;m"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# If the level is not set, assume 5 (notice)
|
||||
if [[ -z $level ]]; then
|
||||
level=5
|
||||
fi
|
||||
|
||||
case $logtype in
|
||||
# If a valid logtype is passed
|
||||
emergency|alert|critical|error|warning|notice|info|debug )
|
||||
# We do nothing here
|
||||
;;
|
||||
# In all other cases
|
||||
* )
|
||||
# Second argument was not a log level, so manually set it to notice
|
||||
# and include the first parameter in the message
|
||||
logtype='notice'
|
||||
msg="${(@)@:3}"
|
||||
;;
|
||||
esac
|
||||
|
||||
if [[ $_log_levels[$logtype] > $level ]]; then
|
||||
# The message being recorded is for a higher log level than the one
|
||||
# currently being recorded, so gracefully exit
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Output the message to the logfile
|
||||
echo "[$(echo $logtype | tr '[a-z]' '[A-Z]')] [$(date '+%Y-%m-%d %H:%M:%S')] $msg" >> $file
|
||||
}
|
||||
|
||||
###
|
||||
# The main lumberjack process
|
||||
###
|
||||
function _lumberjack() {
|
||||
local help version logfile loglevel dir statefile state
|
||||
|
||||
# Create the state directory if it doesn't exist
|
||||
dir="${ZDOTDIR:-$HOME}/.lumberjack"
|
||||
if [[ ! -d $dir ]]; then
|
||||
mkdir -p $dir
|
||||
fi
|
||||
|
||||
# If a statefile already exists, load the level and file
|
||||
statefile="$dir/$PPID"
|
||||
if [[ -f $statefile ]]; then
|
||||
state=$(cat $statefile)
|
||||
level="$state[1]"
|
||||
file="${(@)state:2}"
|
||||
fi
|
||||
|
||||
# Parse CLI options
|
||||
zparseopts -D h=help -help=help \
|
||||
v=version -version=version \
|
||||
f:=logfile -file:=logfile \
|
||||
l:=loglevel -level:=loglevel
|
||||
|
||||
# If the help option is passed, output usage information and exit
|
||||
if [[ -n $help ]]; then
|
||||
_lumberjack_usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# If the version option is passed, output the version and exit
|
||||
if [[ -n $version ]]; then
|
||||
echo "0.1.1"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# If the logfile option is passed, set the current logfile
|
||||
# for the parent process ID
|
||||
if [[ -n $logfile ]]; then
|
||||
shift logfile
|
||||
file=$logfile
|
||||
|
||||
# Create the log file if it doesn't exist
|
||||
if [[ ! -f $file ]]; then
|
||||
touch $file
|
||||
fi
|
||||
fi
|
||||
|
||||
# If the loglevel option is passed, set the current loglevel
|
||||
# for the parent process ID
|
||||
if [[ -n $loglevel ]]; then
|
||||
shift loglevel
|
||||
level=$_log_levels[$loglevel]
|
||||
fi
|
||||
|
||||
if [[ -z $level ]]; then
|
||||
level=5
|
||||
fi
|
||||
|
||||
# Check if we're setting options rather than logging
|
||||
if [[ -n $logfile || -n $loglevel ]]; then
|
||||
# Store the state
|
||||
echo "$level $file" >! $statefile
|
||||
|
||||
# Exit gracefully
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Log the message
|
||||
_lumberjack_message "$level" "$file" "$@"
|
||||
}
|
||||
|
||||
_lumberjack "$@"
|
||||
|
986
bin/mo
Executable file
986
bin/mo
Executable file
|
@ -0,0 +1,986 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
#/ Mo is a mustache template rendering software written in bash. It inserts
|
||||
#/ environment variables into templates.
|
||||
#/
|
||||
#/ Simply put, mo will change {{VARIABLE}} into the value of that
|
||||
#/ environment variable. You can use {{#VARIABLE}}content{{/VARIABLE}} to
|
||||
#/ conditionally display content or iterate over the values of an array.
|
||||
#/
|
||||
#/ Learn more about mustache templates at https://mustache.github.io/
|
||||
#/
|
||||
#/ Simple usage:
|
||||
#/
|
||||
#/ mo [--false] [--help] [--source=FILE] filenames...
|
||||
#/
|
||||
#/ --fail-not-set - Fail upon expansion of an unset variable.
|
||||
#/ --false - Treat the string "false" as empty for conditionals.
|
||||
#/ --help - This message.
|
||||
#/ --source=FILE - Load FILE into the environment before processing templates.
|
||||
#
|
||||
# Mo is under a MIT style licence with an additional non-advertising clause.
|
||||
# See LICENSE.md for the full text.
|
||||
#
|
||||
# This is open source! Please feel free to contribute.
|
||||
#
|
||||
# https://github.com/tests-always-included/mo
|
||||
|
||||
|
||||
# Public: Template parser function. Writes templates to stdout.
|
||||
#
|
||||
# $0 - Name of the mo file, used for getting the help message.
|
||||
# --fail-not-set - Fail upon expansion of an unset variable. Default behavior
|
||||
# is to silently ignore and expand into empty string.
|
||||
# --false - Treat "false" as an empty value. You may set the
|
||||
# MO_FALSE_IS_EMPTY environment variable instead to a non-empty
|
||||
# value to enable this behavior.
|
||||
# --help - Display a help message.
|
||||
# --source=FILE - Source a file into the environment before processint
|
||||
# template files.
|
||||
# -- - Used to indicate the end of options. You may optionally
|
||||
# use this when filenames may start with two hyphens.
|
||||
# $@ - Filenames to parse.
|
||||
#
|
||||
# Mo uses the following environment variables:
|
||||
#
|
||||
# MO_FAIL_ON_UNSET - When set to a non-empty value, expansion of an unset
|
||||
# env variable will be aborted with an error.
|
||||
# MO_FALSE_IS_EMPTY - When set to a non-empty value, the string "false"
|
||||
# will be treated as an empty value for the purposes
|
||||
# of conditionals.
|
||||
# MO_ORIGINAL_COMMAND - Used to find the `mo` program in order to generate
|
||||
# a help message.
|
||||
#
|
||||
# Returns nothing.
|
||||
mo() (
|
||||
# This function executes in a subshell so IFS is reset.
|
||||
# Namespace this variable so we don't conflict with desired values.
|
||||
local moContent f2source files doubleHyphens
|
||||
|
||||
IFS=$' \n\t'
|
||||
files=()
|
||||
doubleHyphens=false
|
||||
|
||||
if [[ $# -gt 0 ]]; then
|
||||
for arg in "$@"; do
|
||||
if $doubleHyphens; then
|
||||
# After we encounter two hyphens together, all the rest
|
||||
# of the arguments are files.
|
||||
files=("${files[@]}" "$arg")
|
||||
else
|
||||
case "$arg" in
|
||||
-h|--h|--he|--hel|--help|-\?)
|
||||
moUsage "$0"
|
||||
exit 0
|
||||
;;
|
||||
|
||||
--fail-not-set)
|
||||
# shellcheck disable=SC2030
|
||||
MO_FAIL_ON_UNSET=true
|
||||
;;
|
||||
|
||||
--false)
|
||||
# shellcheck disable=SC2030
|
||||
MO_FALSE_IS_EMPTY=true
|
||||
;;
|
||||
|
||||
--source=*)
|
||||
f2source="${arg#--source=}"
|
||||
|
||||
if [[ -f "$f2source" ]]; then
|
||||
# shellcheck disable=SC1090
|
||||
. "$f2source"
|
||||
else
|
||||
echo "No such file: $f2source" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
|
||||
--)
|
||||
# Set a flag indicating we've encountered double hyphens
|
||||
doubleHyphens=true
|
||||
;;
|
||||
|
||||
*)
|
||||
# Every arg that is not a flag or a option should be a file
|
||||
files=(${files[@]+"${files[@]}"} "$arg")
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
moGetContent moContent "${files[@]}" || return 1
|
||||
moParse "$moContent" "" true
|
||||
)
|
||||
|
||||
|
||||
# Internal: Scan content until the right end tag is found. Creates an array
|
||||
# with the following members:
|
||||
#
|
||||
# [0] = Content before end tag
|
||||
# [1] = End tag (complete tag)
|
||||
# [2] = Content after end tag
|
||||
#
|
||||
# Everything using this function uses the "standalone tags" logic.
|
||||
#
|
||||
# $1 - Name of variable for the array
|
||||
# $2 - Content
|
||||
# $3 - Name of end tag
|
||||
# $4 - If -z, do standalone tag processing before finishing
|
||||
#
|
||||
# Returns nothing.
|
||||
moFindEndTag() {
|
||||
local content remaining scanned standaloneBytes tag
|
||||
|
||||
#: Find open tags
|
||||
scanned=""
|
||||
moSplit content "$2" '{{' '}}'
|
||||
|
||||
while [[ "${#content[@]}" -gt 1 ]]; do
|
||||
moTrimWhitespace tag "${content[1]}"
|
||||
|
||||
#: Restore content[1] before we start using it
|
||||
content[1]='{{'"${content[1]}"'}}'
|
||||
|
||||
case $tag in
|
||||
'#'* | '^'*)
|
||||
#: Start another block
|
||||
scanned="${scanned}${content[0]}${content[1]}"
|
||||
moTrimWhitespace tag "${tag:1}"
|
||||
moFindEndTag content "${content[2]}" "$tag" "loop"
|
||||
scanned="${scanned}${content[0]}${content[1]}"
|
||||
remaining=${content[2]}
|
||||
;;
|
||||
|
||||
'/'*)
|
||||
#: End a block - could be ours
|
||||
moTrimWhitespace tag "${tag:1}"
|
||||
scanned="$scanned${content[0]}"
|
||||
|
||||
if [[ "$tag" == "$3" ]]; then
|
||||
#: Found our end tag
|
||||
if [[ -z "${4-}" ]] && moIsStandalone standaloneBytes "$scanned" "${content[2]}" true; then
|
||||
#: This is also a standalone tag - clean up whitespace
|
||||
#: and move those whitespace bytes to the "tag" element
|
||||
standaloneBytes=( $standaloneBytes )
|
||||
content[1]="${scanned:${standaloneBytes[0]}}${content[1]}${content[2]:0:${standaloneBytes[1]}}"
|
||||
scanned="${scanned:0:${standaloneBytes[0]}}"
|
||||
content[2]="${content[2]:${standaloneBytes[1]}}"
|
||||
fi
|
||||
|
||||
local "$1" && moIndirectArray "$1" "$scanned" "${content[1]}" "${content[2]}"
|
||||
return 0
|
||||
fi
|
||||
|
||||
scanned="$scanned${content[1]}"
|
||||
remaining=${content[2]}
|
||||
;;
|
||||
|
||||
*)
|
||||
#: Ignore all other tags
|
||||
scanned="${scanned}${content[0]}${content[1]}"
|
||||
remaining=${content[2]}
|
||||
;;
|
||||
esac
|
||||
|
||||
moSplit content "$remaining" '{{' '}}'
|
||||
done
|
||||
|
||||
#: Did not find our closing tag
|
||||
scanned="$scanned${content[0]}"
|
||||
local "$1" && moIndirectArray "$1" "${scanned}" "" ""
|
||||
}
|
||||
|
||||
|
||||
# Internal: Find the first index of a substring. If not found, sets the
|
||||
# index to -1.
|
||||
#
|
||||
# $1 - Destination variable for the index
|
||||
# $2 - Haystack
|
||||
# $3 - Needle
|
||||
#
|
||||
# Returns nothing.
|
||||
moFindString() {
|
||||
local pos string
|
||||
|
||||
string=${2%%$3*}
|
||||
[[ "$string" == "$2" ]] && pos=-1 || pos=${#string}
|
||||
local "$1" && moIndirect "$1" "$pos"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Generate a dotted name based on current context and target name.
|
||||
#
|
||||
# $1 - Target variable to store results
|
||||
# $2 - Context name
|
||||
# $3 - Desired variable name
|
||||
#
|
||||
# Returns nothing.
|
||||
moFullTagName() {
|
||||
if [[ -z "${2-}" ]] || [[ "$2" == *.* ]]; then
|
||||
local "$1" && moIndirect "$1" "$3"
|
||||
else
|
||||
local "$1" && moIndirect "$1" "${2}.${3}"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Internal: Fetches the content to parse into a variable. Can be a list of
|
||||
# partials for files or the content from stdin.
|
||||
#
|
||||
# $1 - Variable name to assign this content back as
|
||||
# $2-@ - File names (optional)
|
||||
#
|
||||
# Returns nothing.
|
||||
moGetContent() {
|
||||
local content filename target
|
||||
|
||||
target=$1
|
||||
shift
|
||||
if [[ "${#@}" -gt 0 ]]; then
|
||||
content=""
|
||||
|
||||
for filename in "$@"; do
|
||||
#: This is so relative paths work from inside template files
|
||||
content="$content"'{{>'"$filename"'}}'
|
||||
done
|
||||
else
|
||||
moLoadFile content /dev/stdin || return 1
|
||||
fi
|
||||
|
||||
local "$target" && moIndirect "$target" "$content"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Indent a string, placing the indent at the beginning of every
|
||||
# line that has any content.
|
||||
#
|
||||
# $1 - Name of destination variable to get an array of lines
|
||||
# $2 - The indent string
|
||||
# $3 - The string to reindent
|
||||
#
|
||||
# Returns nothing.
|
||||
moIndentLines() {
|
||||
local content fragment len posN posR result trimmed
|
||||
|
||||
result=""
|
||||
|
||||
#: Remove the period from the end of the string.
|
||||
len=$((${#3} - 1))
|
||||
content=${3:0:$len}
|
||||
|
||||
if [[ -z "${2-}" ]]; then
|
||||
local "$1" && moIndirect "$1" "$content"
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
moFindString posN "$content" $'\n'
|
||||
moFindString posR "$content" $'\r'
|
||||
|
||||
while [[ "$posN" -gt -1 ]] || [[ "$posR" -gt -1 ]]; do
|
||||
if [[ "$posN" -gt -1 ]]; then
|
||||
fragment="${content:0:$posN + 1}"
|
||||
content=${content:$posN + 1}
|
||||
else
|
||||
fragment="${content:0:$posR + 1}"
|
||||
content=${content:$posR + 1}
|
||||
fi
|
||||
|
||||
moTrimChars trimmed "$fragment" false true " " $'\t' $'\n' $'\r'
|
||||
|
||||
if [[ -n "$trimmed" ]]; then
|
||||
fragment="$2$fragment"
|
||||
fi
|
||||
|
||||
result="$result$fragment"
|
||||
|
||||
moFindString posN "$content" $'\n'
|
||||
moFindString posR "$content" $'\r'
|
||||
|
||||
# If the content ends in a newline, do not indent.
|
||||
if [[ "$posN" -eq ${#content} ]]; then
|
||||
# Special clause for \r\n
|
||||
if [[ "$posR" -eq "$((posN - 1))" ]]; then
|
||||
posR=-1
|
||||
fi
|
||||
|
||||
posN=-1
|
||||
fi
|
||||
|
||||
if [[ "$posR" -eq ${#content} ]]; then
|
||||
posR=-1
|
||||
fi
|
||||
done
|
||||
|
||||
moTrimChars trimmed "$content" false true " " $'\t'
|
||||
|
||||
if [[ -n "$trimmed" ]]; then
|
||||
content="$2$content"
|
||||
fi
|
||||
|
||||
result="$result$content"
|
||||
|
||||
local "$1" && moIndirect "$1" "$result"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Send a variable up to the parent of the caller of this function.
|
||||
#
|
||||
# $1 - Variable name
|
||||
# $2 - Value
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# callFunc () {
|
||||
# local "$1" && moIndirect "$1" "the value"
|
||||
# }
|
||||
# callFunc dest
|
||||
# echo "$dest" # writes "the value"
|
||||
#
|
||||
# Returns nothing.
|
||||
moIndirect() {
|
||||
unset -v "$1"
|
||||
printf -v "$1" '%s' "$2"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Send an array as a variable up to caller of a function
|
||||
#
|
||||
# $1 - Variable name
|
||||
# $2-@ - Array elements
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# callFunc () {
|
||||
# local myArray=(one two three)
|
||||
# local "$1" && moIndirectArray "$1" "${myArray[@]}"
|
||||
# }
|
||||
# callFunc dest
|
||||
# echo "${dest[@]}" # writes "one two three"
|
||||
#
|
||||
# Returns nothing.
|
||||
moIndirectArray() {
|
||||
unset -v "$1"
|
||||
|
||||
# IFS must be set to a string containing space or unset in order for
|
||||
# the array slicing to work regardless of the current IFS setting on
|
||||
# bash 3. This is detailed further at
|
||||
# https://github.com/fidian/gg-core/pull/7
|
||||
eval "$(printf "IFS= %s=(\"\${@:2}\") IFS=%q" "$1" "$IFS")"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Determine if a given environment variable exists and if it is
|
||||
# an array.
|
||||
#
|
||||
# $1 - Name of environment variable
|
||||
#
|
||||
# Be extremely careful. Even if strict mode is enabled, it is not honored
|
||||
# in newer versions of Bash. Any errors that crop up here will not be
|
||||
# caught automatically.
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# var=(abc)
|
||||
# if moIsArray var; then
|
||||
# echo "This is an array"
|
||||
# echo "Make sure you don't accidentally use \$var"
|
||||
# fi
|
||||
#
|
||||
# Returns 0 if the name is not empty, 1 otherwise.
|
||||
moIsArray() {
|
||||
# Namespace this variable so we don't conflict with what we're testing.
|
||||
local moTestResult
|
||||
|
||||
moTestResult=$(declare -p "$1" 2>/dev/null) || return 1
|
||||
[[ "${moTestResult:0:10}" == "declare -a" ]] && return 0
|
||||
[[ "${moTestResult:0:10}" == "declare -A" ]] && return 0
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
# Internal: Determine if the given name is a defined function.
|
||||
#
|
||||
# $1 - Function name to check
|
||||
#
|
||||
# Be extremely careful. Even if strict mode is enabled, it is not honored
|
||||
# in newer versions of Bash. Any errors that crop up here will not be
|
||||
# caught automatically.
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# moo () {
|
||||
# echo "This is a function"
|
||||
# }
|
||||
# if moIsFunction moo; then
|
||||
# echo "moo is a defined function"
|
||||
# fi
|
||||
#
|
||||
# Returns 0 if the name is a function, 1 otherwise.
|
||||
moIsFunction() {
|
||||
local functionList functionName
|
||||
|
||||
functionList=$(declare -F)
|
||||
functionList=( ${functionList//declare -f /} )
|
||||
|
||||
for functionName in "${functionList[@]}"; do
|
||||
if [[ "$functionName" == "$1" ]]; then
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
|
||||
# Internal: Determine if the tag is a standalone tag based on whitespace
|
||||
# before and after the tag.
|
||||
#
|
||||
# Passes back a string containing two numbers in the format "BEFORE AFTER"
|
||||
# like "27 10". It indicates the number of bytes remaining in the "before"
|
||||
# string (27) and the number of bytes to trim in the "after" string (10).
|
||||
# Useful for string manipulation:
|
||||
#
|
||||
# $1 - Variable to set for passing data back
|
||||
# $2 - Content before the tag
|
||||
# $3 - Content after the tag
|
||||
# $4 - true/false: is this the beginning of the content?
|
||||
#
|
||||
# Examples
|
||||
#
|
||||
# moIsStandalone RESULT "$before" "$after" false || return 0
|
||||
# RESULT_ARRAY=( $RESULT )
|
||||
# echo "${before:0:${RESULT_ARRAY[0]}}...${after:${RESULT_ARRAY[1]}}"
|
||||
#
|
||||
# Returns nothing.
|
||||
moIsStandalone() {
|
||||
local afterTrimmed beforeTrimmed char
|
||||
|
||||
moTrimChars beforeTrimmed "$2" false true " " $'\t'
|
||||
moTrimChars afterTrimmed "$3" true false " " $'\t'
|
||||
char=$((${#beforeTrimmed} - 1))
|
||||
char=${beforeTrimmed:$char}
|
||||
|
||||
# If the content before didn't end in a newline
|
||||
if [[ "$char" != $'\n' ]] && [[ "$char" != $'\r' ]]; then
|
||||
# and there was content or this didn't start the file
|
||||
if [[ -n "$char" ]] || ! $4; then
|
||||
# then this is not a standalone tag.
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
|
||||
char=${afterTrimmed:0:1}
|
||||
|
||||
# If the content after doesn't start with a newline and it is something
|
||||
if [[ "$char" != $'\n' ]] && [[ "$char" != $'\r' ]] && [[ -n "$char" ]]; then
|
||||
# then this is not a standalone tag.
|
||||
return 2
|
||||
fi
|
||||
|
||||
if [[ "$char" == $'\r' ]] && [[ "${afterTrimmed:1:1}" == $'\n' ]]; then
|
||||
char="$char"$'\n'
|
||||
fi
|
||||
|
||||
local "$1" && moIndirect "$1" "$((${#beforeTrimmed})) $((${#3} + ${#char} - ${#afterTrimmed}))"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Join / implode an array
|
||||
#
|
||||
# $1 - Variable name to receive the joined content
|
||||
# $2 - Joiner
|
||||
# $3-$* - Elements to join
|
||||
#
|
||||
# Returns nothing.
|
||||
moJoin() {
|
||||
local joiner part result target
|
||||
|
||||
target=$1
|
||||
joiner=$2
|
||||
result=$3
|
||||
shift 3
|
||||
|
||||
for part in "$@"; do
|
||||
result="$result$joiner$part"
|
||||
done
|
||||
|
||||
local "$target" && moIndirect "$target" "$result"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Read a file into a variable.
|
||||
#
|
||||
# $1 - Variable name to receive the file's content
|
||||
# $2 - Filename to load
|
||||
#
|
||||
# Returns nothing.
|
||||
moLoadFile() {
|
||||
local content len
|
||||
|
||||
# The subshell removes any trailing newlines. We forcibly add
|
||||
# a dot to the content to preserve all newlines.
|
||||
# As a future optimization, it would be worth considering removing
|
||||
# cat and replacing this with a read loop.
|
||||
|
||||
content=$(cat -- "$2" && echo '.') || return 1
|
||||
len=$((${#content} - 1))
|
||||
content=${content:0:$len} # Remove last dot
|
||||
|
||||
local "$1" && moIndirect "$1" "$content"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Process a chunk of content some number of times. Writes output
|
||||
# to stdout.
|
||||
#
|
||||
# $1 - Content to parse repeatedly
|
||||
# $2 - Tag prefix (context name)
|
||||
# $3-@ - Names to insert into the parsed content
|
||||
#
|
||||
# Returns nothing.
|
||||
moLoop() {
|
||||
local content context contextBase
|
||||
|
||||
content=$1
|
||||
contextBase=$2
|
||||
shift 2
|
||||
|
||||
while [[ "${#@}" -gt 0 ]]; do
|
||||
moFullTagName context "$contextBase" "$1"
|
||||
moParse "$content" "$context" false
|
||||
shift
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
# Internal: Parse a block of text, writing the result to stdout.
|
||||
#
|
||||
# $1 - Block of text to change
|
||||
# $2 - Current name (the variable NAME for what {{.}} means)
|
||||
# $3 - true when no content before this, false otherwise
|
||||
#
|
||||
# Returns nothing.
|
||||
moParse() {
|
||||
# Keep naming variables mo* here to not overwrite needed variables
|
||||
# used in the string replacements
|
||||
local moBlock moContent moCurrent moIsBeginning moNextIsBeginning moTag
|
||||
|
||||
moCurrent=$2
|
||||
moIsBeginning=$3
|
||||
|
||||
# Find open tags
|
||||
moSplit moContent "$1" '{{' '}}'
|
||||
|
||||
while [[ "${#moContent[@]}" -gt 1 ]]; do
|
||||
moTrimWhitespace moTag "${moContent[1]}"
|
||||
moNextIsBeginning=false
|
||||
|
||||
case $moTag in
|
||||
'#'*)
|
||||
# Loop, if/then, or pass content through function
|
||||
# Sets context
|
||||
moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning"
|
||||
moTrimWhitespace moTag "${moTag:1}"
|
||||
moFindEndTag moBlock "$moContent" "$moTag"
|
||||
moFullTagName moTag "$moCurrent" "$moTag"
|
||||
|
||||
if moTest "$moTag"; then
|
||||
# Show / loop / pass through function
|
||||
if moIsFunction "$moTag"; then
|
||||
#: Consider piping the output to moGetContent
|
||||
#: so the lambda does not execute in a subshell?
|
||||
moContent=$($moTag "${moBlock[0]}")
|
||||
moParse "$moContent" "$moCurrent" false
|
||||
moContent="${moBlock[2]}"
|
||||
elif moIsArray "$moTag"; then
|
||||
eval "moLoop \"\${moBlock[0]}\" \"$moTag\" \"\${!${moTag}[@]}\""
|
||||
else
|
||||
moParse "${moBlock[0]}" "$moCurrent" false
|
||||
fi
|
||||
fi
|
||||
|
||||
moContent="${moBlock[2]}"
|
||||
;;
|
||||
|
||||
'>'*)
|
||||
# Load partial - get name of file relative to cwd
|
||||
moPartial moContent "${moContent[@]}" "$moIsBeginning" "$moCurrent"
|
||||
moNextIsBeginning=${moContent[1]}
|
||||
moContent=${moContent[0]}
|
||||
;;
|
||||
|
||||
'/'*)
|
||||
# Closing tag - If hit in this loop, we simply ignore
|
||||
# Matching tags are found in moFindEndTag
|
||||
moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning"
|
||||
;;
|
||||
|
||||
'^'*)
|
||||
# Display section if named thing does not exist
|
||||
moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning"
|
||||
moTrimWhitespace moTag "${moTag:1}"
|
||||
moFindEndTag moBlock "$moContent" "$moTag"
|
||||
moFullTagName moTag "$moCurrent" "$moTag"
|
||||
|
||||
if ! moTest "$moTag"; then
|
||||
moParse "${moBlock[0]}" "$moCurrent" false "$moCurrent"
|
||||
fi
|
||||
|
||||
moContent="${moBlock[2]}"
|
||||
;;
|
||||
|
||||
'!'*)
|
||||
# Comment - ignore the tag content entirely
|
||||
# Trim spaces/tabs before the comment
|
||||
moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning"
|
||||
;;
|
||||
|
||||
.)
|
||||
# Current content (environment variable or function)
|
||||
moStandaloneDenied moContent "${moContent[@]}"
|
||||
moShow "$moCurrent" "$moCurrent"
|
||||
;;
|
||||
|
||||
'=')
|
||||
# Change delimiters
|
||||
# Any two non-whitespace sequences separated by whitespace.
|
||||
# This tag is ignored.
|
||||
moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning"
|
||||
;;
|
||||
|
||||
'{'*)
|
||||
# Unescaped - split on }}} not }}
|
||||
moStandaloneDenied moContent "${moContent[@]}"
|
||||
moContent="${moTag:1}"'}}'"$moContent"
|
||||
moSplit moContent "$moContent" '}}}'
|
||||
moTrimWhitespace moTag "${moContent[0]}"
|
||||
moFullTagName moTag "$moCurrent" "$moTag"
|
||||
moContent=${moContent[1]}
|
||||
|
||||
# Now show the value
|
||||
moShow "$moTag" "$moCurrent"
|
||||
;;
|
||||
|
||||
'&'*)
|
||||
# Unescaped
|
||||
moStandaloneDenied moContent "${moContent[@]}"
|
||||
moTrimWhitespace moTag "${moTag:1}"
|
||||
moFullTagName moTag "$moCurrent" "$moTag"
|
||||
moShow "$moTag" "$moCurrent"
|
||||
;;
|
||||
|
||||
*)
|
||||
# Normal environment variable or function call
|
||||
moStandaloneDenied moContent "${moContent[@]}"
|
||||
moFullTagName moTag "$moCurrent" "$moTag"
|
||||
moShow "$moTag" "$moCurrent"
|
||||
;;
|
||||
esac
|
||||
|
||||
moIsBeginning=$moNextIsBeginning
|
||||
moSplit moContent "$moContent" '{{' '}}'
|
||||
done
|
||||
|
||||
echo -n "${moContent[0]}"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Process a partial.
|
||||
#
|
||||
# Indentation should be applied to the entire partial.
|
||||
#
|
||||
# This sends back the "is beginning" flag because the newline after a
|
||||
# standalone partial is consumed. That newline is very important in the middle
|
||||
# of content. We send back this flag to reset the processing loop's
|
||||
# `moIsBeginning` variable, so the software thinks we are back at the
|
||||
# beginning of a file and standalone processing continues to work.
|
||||
#
|
||||
# Prefix all variables.
|
||||
#
|
||||
# $1 - Name of destination variable. Element [0] is the content, [1] is the
|
||||
# true/false flag indicating if we are at the beginning of content.
|
||||
# $2 - Content before the tag that was not yet written
|
||||
# $3 - Tag content
|
||||
# $4 - Content after the tag
|
||||
# $5 - true/false: is this the beginning of the content?
|
||||
# $6 - Current context name
|
||||
#
|
||||
# Returns nothing.
|
||||
moPartial() {
|
||||
# Namespace variables here to prevent conflicts.
|
||||
local moContent moFilename moIndent moIsBeginning moPartial moStandalone moUnindented
|
||||
|
||||
if moIsStandalone moStandalone "$2" "$4" "$5"; then
|
||||
moStandalone=( $moStandalone )
|
||||
echo -n "${2:0:${moStandalone[0]}}"
|
||||
moIndent=${2:${moStandalone[0]}}
|
||||
moContent=${4:${moStandalone[1]}}
|
||||
moIsBeginning=true
|
||||
else
|
||||
moIndent=""
|
||||
echo -n "$2"
|
||||
moContent=$4
|
||||
moIsBeginning=$5
|
||||
fi
|
||||
|
||||
moTrimWhitespace moFilename "${3:1}"
|
||||
|
||||
# Execute in subshell to preserve current cwd and environment
|
||||
(
|
||||
# It would be nice to remove `dirname` and use a function instead,
|
||||
# but that's difficult when you're only given filenames.
|
||||
cd "$(dirname -- "$moFilename")" || exit 1
|
||||
moUnindented="$(
|
||||
moLoadFile moPartial "${moFilename##*/}" || exit 1
|
||||
moParse "${moPartial}" "$6" true
|
||||
|
||||
# Fix bash handling of subshells and keep trailing whitespace.
|
||||
# This is removed in moIndentLines.
|
||||
echo -n "."
|
||||
)" || exit 1
|
||||
moIndentLines moPartial "$moIndent" "$moUnindented"
|
||||
echo -n "$moPartial"
|
||||
) || exit 1
|
||||
|
||||
# If this is a standalone tag, the trailing newline after the tag is
|
||||
# removed and the contents of the partial are added, which typically
|
||||
# contain a newline. We need to send a signal back to the processing
|
||||
# loop that the moIsBeginning flag needs to be turned on again.
|
||||
#
|
||||
# [0] is the content, [1] is that flag.
|
||||
local "$1" && moIndirectArray "$1" "$moContent" "$moIsBeginning"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Show an environment variable or the output of a function to
|
||||
# stdout.
|
||||
#
|
||||
# Limit/prefix any variables used.
|
||||
#
|
||||
# $1 - Name of environment variable or function
|
||||
# $2 - Current context
|
||||
#
|
||||
# Returns nothing.
|
||||
moShow() {
|
||||
# Namespace these variables
|
||||
local moJoined moNameParts
|
||||
|
||||
if moIsFunction "$1"; then
|
||||
CONTENT=$($1 "")
|
||||
moParse "$CONTENT" "$2" false
|
||||
return 0
|
||||
fi
|
||||
|
||||
moSplit moNameParts "$1" "."
|
||||
|
||||
if [[ -z "${moNameParts[1]}" ]]; then
|
||||
if moIsArray "$1"; then
|
||||
eval moJoin moJoined "," "\${$1[@]}"
|
||||
echo -n "$moJoined"
|
||||
else
|
||||
# shellcheck disable=SC2031
|
||||
if [[ -z "$MO_FAIL_ON_UNSET" ]] || moTestVarSet "$1"; then
|
||||
echo -n "${!1}"
|
||||
else
|
||||
echo "Env variable not set: $1" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# Further subindexes are disallowed
|
||||
eval "echo -n \"\${${moNameParts[0]}[${moNameParts[1]%%.*}]}\""
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Internal: Split a larger string into an array.
|
||||
#
|
||||
# $1 - Destination variable
|
||||
# $2 - String to split
|
||||
# $3 - Starting delimiter
|
||||
# $4 - Ending delimiter (optional)
|
||||
#
|
||||
# Returns nothing.
|
||||
moSplit() {
|
||||
local pos result
|
||||
|
||||
result=( "$2" )
|
||||
moFindString pos "${result[0]}" "$3"
|
||||
|
||||
if [[ "$pos" -ne -1 ]]; then
|
||||
# The first delimiter was found
|
||||
result[1]=${result[0]:$pos + ${#3}}
|
||||
result[0]=${result[0]:0:$pos}
|
||||
|
||||
if [[ -n "${4-}" ]]; then
|
||||
moFindString pos "${result[1]}" "$4"
|
||||
|
||||
if [[ "$pos" -ne -1 ]]; then
|
||||
# The second delimiter was found
|
||||
result[2]="${result[1]:$pos + ${#4}}"
|
||||
result[1]="${result[1]:0:$pos}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
local "$1" && moIndirectArray "$1" "${result[@]}"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Handle the content for a standalone tag. This means removing
|
||||
# whitespace (not newlines) before a tag and whitespace and a newline after
|
||||
# a tag. That is, assuming, that the line is otherwise empty.
|
||||
#
|
||||
# $1 - Name of destination "content" variable.
|
||||
# $2 - Content before the tag that was not yet written
|
||||
# $3 - Tag content (not used)
|
||||
# $4 - Content after the tag
|
||||
# $5 - true/false: is this the beginning of the content?
|
||||
#
|
||||
# Returns nothing.
|
||||
moStandaloneAllowed() {
|
||||
local bytes
|
||||
|
||||
if moIsStandalone bytes "$2" "$4" "$5"; then
|
||||
bytes=( $bytes )
|
||||
echo -n "${2:0:${bytes[0]}}"
|
||||
local "$1" && moIndirect "$1" "${4:${bytes[1]}}"
|
||||
else
|
||||
echo -n "$2"
|
||||
local "$1" && moIndirect "$1" "$4"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Internal: Handle the content for a tag that is never "standalone". No
|
||||
# adjustments are made for newlines and whitespace.
|
||||
#
|
||||
# $1 - Name of destination "content" variable.
|
||||
# $2 - Content before the tag that was not yet written
|
||||
# $3 - Tag content (not used)
|
||||
# $4 - Content after the tag
|
||||
#
|
||||
# Returns nothing.
|
||||
moStandaloneDenied() {
|
||||
echo -n "$2"
|
||||
local "$1" && moIndirect "$1" "$4"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Determines if the named thing is a function or if it is a
|
||||
# non-empty environment variable. When MO_FALSE_IS_EMPTY is set to a
|
||||
# non-empty value, then "false" is also treated is an empty value.
|
||||
#
|
||||
# Do not use variables without prefixes here if possible as this needs to
|
||||
# check if any name exists in the environment
|
||||
#
|
||||
# $1 - Name of environment variable or function
|
||||
# $2 - Current value (our context)
|
||||
# MO_FALSE_IS_EMPTY - When set to a non-empty value, this will say the
|
||||
# string value "false" is empty.
|
||||
#
|
||||
# Returns 0 if the name is not empty, 1 otherwise. When MO_FALSE_IS_EMPTY
|
||||
# is set, this returns 1 if the name is "false".
|
||||
moTest() {
|
||||
# Test for functions
|
||||
moIsFunction "$1" && return 0
|
||||
|
||||
if moIsArray "$1"; then
|
||||
# Arrays must have at least 1 element
|
||||
eval "[[ \"\${#${1}[@]}\" -gt 0 ]]" && return 0
|
||||
else
|
||||
# If MO_FALSE_IS_EMPTY is set, then return 1 if the value of
|
||||
# the variable is "false".
|
||||
# shellcheck disable=SC2031
|
||||
[[ -n "${MO_FALSE_IS_EMPTY-}" ]] && [[ "${!1-}" == "false" ]] && return 1
|
||||
|
||||
# Environment variables must not be empty
|
||||
[[ -n "${!1-}" ]] && return 0
|
||||
fi
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Internal: Determine if a variable is assigned, even if it is assigned an empty
|
||||
# value.
|
||||
#
|
||||
# $1 - Variable name to check.
|
||||
#
|
||||
# Returns true (0) if the variable is set, 1 if the variable is unset.
|
||||
moTestVarSet() {
|
||||
[[ "${!1-a}" == "${!1-b}" ]]
|
||||
}
|
||||
|
||||
|
||||
# Internal: Trim the leading whitespace only.
|
||||
#
|
||||
# $1 - Name of destination variable
|
||||
# $2 - The string
|
||||
# $3 - true/false - trim front?
|
||||
# $4 - true/false - trim end?
|
||||
# $5-@ - Characters to trim
|
||||
#
|
||||
# Returns nothing.
|
||||
moTrimChars() {
|
||||
local back current front last target varName
|
||||
|
||||
target=$1
|
||||
current=$2
|
||||
front=$3
|
||||
back=$4
|
||||
last=""
|
||||
shift 4 # Remove target, string, trim front flag, trim end flag
|
||||
|
||||
while [[ "$current" != "$last" ]]; do
|
||||
last=$current
|
||||
|
||||
for varName in "$@"; do
|
||||
$front && current="${current/#$varName}"
|
||||
$back && current="${current/%$varName}"
|
||||
done
|
||||
done
|
||||
|
||||
local "$target" && moIndirect "$target" "$current"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Trim leading and trailing whitespace from a string.
|
||||
#
|
||||
# $1 - Name of variable to store trimmed string
|
||||
# $2 - The string
|
||||
#
|
||||
# Returns nothing.
|
||||
moTrimWhitespace() {
|
||||
local result
|
||||
|
||||
moTrimChars result "$2" true true $'\r' $'\n' $'\t' " "
|
||||
local "$1" && moIndirect "$1" "$result"
|
||||
}
|
||||
|
||||
|
||||
# Internal: Displays the usage for mo. Pulls this from the file that
|
||||
# contained the `mo` function. Can only work when the right filename
|
||||
# comes is the one argument, and that only happens when `mo` is called
|
||||
# with `$0` set to this file.
|
||||
#
|
||||
# $1 - Filename that has the help message
|
||||
#
|
||||
# Returns nothing.
|
||||
moUsage() {
|
||||
grep '^#/' "${MO_ORIGINAL_COMMAND}" | cut -c 4-
|
||||
}
|
||||
|
||||
|
||||
# Save the original command's path for usage later
|
||||
MO_ORIGINAL_COMMAND="$(cd "${BASH_SOURCE[0]%/*}" || exit 1; pwd)/${BASH_SOURCE[0]##*/}"
|
||||
|
||||
# If sourced, load all functions.
|
||||
# If executed, perform the actions as expected.
|
||||
if [[ "$0" == "${BASH_SOURCE[0]}" ]] || [[ -z "${BASH_SOURCE[0]}" ]]; then
|
||||
mo "$@"
|
||||
fi
|
||||
|
490
bin/shml
Executable file
490
bin/shml
Executable file
|
@ -0,0 +1,490 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
#SHML:START
|
||||
#************************************************#
|
||||
# SHML - Shell Markup Language Framework
|
||||
# v1.0.3
|
||||
# (MIT)
|
||||
# by Justin Dorfman - @jdorfman
|
||||
# && Joshua Mervine - @mervinej
|
||||
#
|
||||
# https://maxcdn.github.io/shml/
|
||||
#************************************************#
|
||||
|
||||
# Foreground (Text)
|
||||
##
|
||||
fgcolor() {
|
||||
local __end='\033[39m'
|
||||
local __color=$__end # end by default
|
||||
case "$1" in
|
||||
end|off|reset) __color=$__end;;
|
||||
black|000000) __color='\033[30m';;
|
||||
red|F00BAF) __color='\033[31m';;
|
||||
green|00CD00) __color='\033[32m';;
|
||||
yellow|CDCD00) __color='\033[33m';;
|
||||
blue|0286fe) __color='\033[34m';;
|
||||
magenta|e100cc) __color='\033[35m';;
|
||||
cyan|00d3cf) __color='\033[36m';;
|
||||
gray|e4e4e4) __color='\033[90m';;
|
||||
darkgray|4c4c4c) __color='\033[91m';;
|
||||
lightgreen|00fe00) __color='\033[92m';;
|
||||
lightyellow|f8fe00) __color='\033[93m';;
|
||||
lightblue|3a80b5) __color='\033[94m';;
|
||||
lightmagenta|fe00fe) __color='\033[95m';;
|
||||
lightcyan|00fefe) __color='\033[96m';;
|
||||
white|ffffff) __color='\033[97m';;
|
||||
esac
|
||||
if test "$2"; then
|
||||
echo -en "$__color$2$__end"
|
||||
else
|
||||
echo -en "$__color"
|
||||
fi
|
||||
}
|
||||
|
||||
# Backwards Compatibility
|
||||
color() {
|
||||
fgcolor "$@"
|
||||
}
|
||||
|
||||
# Aliases
|
||||
fgc() {
|
||||
fgcolor "$@"
|
||||
}
|
||||
|
||||
c() {
|
||||
fgcolor "$@"
|
||||
}
|
||||
|
||||
# Background
|
||||
##
|
||||
bgcolor() {
|
||||
local __end='\033[49m'
|
||||
local __color=$__end # end by default
|
||||
case "$1" in
|
||||
end|off|reset) __color=$__end;;
|
||||
black|000000) __color='\033[40m';;
|
||||
red|F00BAF) __color='\033[41m';;
|
||||
green|00CD00) __color='\033[42m';;
|
||||
yellow|CDCD00) __color='\033[43m';;
|
||||
blue|0286fe) __color='\033[44m';;
|
||||
magenta|e100cc) __color='\033[45m';;
|
||||
cyan|00d3cf) __color='\033[46m';;
|
||||
gray|e4e4e4) __color='\033[47m';;
|
||||
darkgray|4c4c4c) __color='\033[100m';;
|
||||
lightred) __color='\033[101m';;
|
||||
lightgreen|00fe00) __color='\033[102m';;
|
||||
lightyellow|f8fe00) __color='\033[103m';;
|
||||
lightblue|3a80b5) __color='\033[104m';;
|
||||
lightmagenta|fe00fe) __color='\033[105m';;
|
||||
lightcyan|00fefe) __color='\033[106m';;
|
||||
white|fffff) __color='\033[107m';;
|
||||
esac
|
||||
|
||||
if test "$2"; then
|
||||
echo -en "$__color$2$__end"
|
||||
else
|
||||
echo -en "$__color"
|
||||
fi
|
||||
}
|
||||
|
||||
#Backwards Compatibility
|
||||
background() {
|
||||
bgcolor "$@"
|
||||
}
|
||||
|
||||
#Aliases
|
||||
bgc() {
|
||||
bgcolor "$@"
|
||||
}
|
||||
|
||||
bg() {
|
||||
bgcolor "$@"
|
||||
}
|
||||
|
||||
## Color Bar
|
||||
color-bar() {
|
||||
if test "$2"; then
|
||||
for i in "$@"; do
|
||||
echo -en "$(background "$i" " ")"
|
||||
done; echo
|
||||
else
|
||||
for i in {16..21}{21..16}; do
|
||||
echo -en "\033[48;5;${i}m \033[0m"
|
||||
done; echo
|
||||
fi
|
||||
}
|
||||
|
||||
#Alises
|
||||
cb() {
|
||||
color-bar "$@"
|
||||
}
|
||||
|
||||
bar() {
|
||||
color-bar "$@"
|
||||
}
|
||||
|
||||
## Attributes
|
||||
##
|
||||
attribute() {
|
||||
local __end='\033[0m'
|
||||
local __attr=$__end # end by default
|
||||
case "$1" in
|
||||
end|off|reset) __attr=$__end;;
|
||||
bold) __attr='\033[1m';;
|
||||
dim) __attr='\033[2m';;
|
||||
underline) __attr='\033[4m';;
|
||||
blink) __attr='\033[5m';;
|
||||
invert) __attr='\033[7m';;
|
||||
hidden) __attr='\033[8m';;
|
||||
esac
|
||||
if test "$2"; then
|
||||
echo -en "$__attr$2$__end"
|
||||
else
|
||||
echo -en "$__attr"
|
||||
fi
|
||||
}
|
||||
a() {
|
||||
attribute "$@"
|
||||
}
|
||||
|
||||
## Elements
|
||||
br() {
|
||||
echo -e "\n\r"
|
||||
}
|
||||
|
||||
tab() {
|
||||
echo -e "\t"
|
||||
}
|
||||
|
||||
indent() {
|
||||
local __len=4
|
||||
if test "$1"; then
|
||||
if [[ $1 =~ $re ]] ; then
|
||||
__len=$1
|
||||
fi
|
||||
fi
|
||||
while [ $__len -gt 0 ]; do
|
||||
echo -n " "
|
||||
__len=$(( $__len - 1 ))
|
||||
done
|
||||
}
|
||||
i() {
|
||||
indent "$@"
|
||||
}
|
||||
|
||||
hr() {
|
||||
local __len=60
|
||||
local __char='-'
|
||||
if ! test "$2"; then
|
||||
re='^[0-9]+$'
|
||||
if [[ $1 =~ $re ]] ; then
|
||||
__len=$1
|
||||
elif test "$1"; then
|
||||
__char=$1
|
||||
fi
|
||||
else
|
||||
__len=$2
|
||||
__char=$1
|
||||
fi
|
||||
while [ $__len -gt 0 ]; do
|
||||
echo -n "$__char"
|
||||
__len=$(( $__len - 1 ))
|
||||
done
|
||||
}
|
||||
|
||||
# Icons
|
||||
##
|
||||
|
||||
icon() {
|
||||
local i='';
|
||||
case "$1" in
|
||||
check|checkmark) i='\xE2\x9C\x93';;
|
||||
X|x|xmark) i='\xE2\x9C\x98';;
|
||||
'<3'|heart) i='\xE2\x9D\xA4';;
|
||||
sun) i='\xE2\x98\x80';;
|
||||
'*'|star) i='\xE2\x98\x85';;
|
||||
darkstar) i='\xE2\x98\x86';;
|
||||
umbrella) i='\xE2\x98\x82';;
|
||||
flag) i='\xE2\x9A\x91';;
|
||||
snow|snowflake) i='\xE2\x9D\x84';;
|
||||
music) i='\xE2\x99\xAB';;
|
||||
scissors) i='\xE2\x9C\x82';;
|
||||
tm|trademark) i='\xE2\x84\xA2';;
|
||||
copyright) i='\xC2\xA9';;
|
||||
apple) i='\xEF\xA3\xBF';;
|
||||
skull|bones) i='\xE2\x98\xA0';;
|
||||
':-)'|':)'|smile|face) i='\xE2\x98\xBA';;
|
||||
*)
|
||||
entity $1; return 0;;
|
||||
esac
|
||||
echo -ne "$i";
|
||||
}
|
||||
emoji() {
|
||||
local i=""
|
||||
case "$1" in
|
||||
|
||||
1F603|smiley|'=)'|':-)'|':)') i='😃';;
|
||||
1F607|innocent|halo) i='😇';;
|
||||
1F602|joy|lol|laughing) i='😂';;
|
||||
1F61B|tongue|'=p'|'=P') i='😛';;
|
||||
1F60A|blush|'^^'|blushing) i='😊';;
|
||||
1F61F|worried|sadface|sad) i='😟';;
|
||||
1F622|cry|crying|tear) i='😢';;
|
||||
1F621|rage|redface) i='😡';;
|
||||
1F44B|wave|hello|goodbye) i='👋';;
|
||||
1F44C|ok_hand|perfect|okay|nice) i='👌';;
|
||||
1F44D|thumbsup|+1|like) i='👍';;
|
||||
1F44E|thumbsdown|-1|no|dislike) i='👎';;
|
||||
1F63A|smiley_cat|happycat) i='😺';;
|
||||
1F431|cat|kitten|:3|kitty) i='🐱';;
|
||||
1F436|dog|puppy) i='🐶';;
|
||||
1F41D|bee|honeybee|bumblebee) i='🐝';;
|
||||
1F437|pig|pighead) i='🐷';;
|
||||
1F435|monkey_face|monkey) i='🐵';;
|
||||
1F42E|cow|happycow) i='🐮';;
|
||||
1F43C|panda_face|panda|shpanda) i='🐼';;
|
||||
1F363|sushi|raw|sashimi) i='🍣';;
|
||||
1F3E0|home|house) i='🏠';;
|
||||
1F453|eyeglasses|bifocals) i='👓';;
|
||||
1F6AC|smoking|smoke|cigarette) i='🚬';;
|
||||
1F525|fire|flame|hot|snapstreak) i='🔥';;
|
||||
1F4A9|hankey|poop|shit) i='💩';;
|
||||
1F37A|beer|homebrew|brew) i='🍺';;
|
||||
1F36A|cookie|biscuit|chocolate) i='🍪';;
|
||||
1F512|lock|padlock|secure) i='🔒';;
|
||||
1F513|unlock|openpadlock) i='🔓';;
|
||||
2B50|star|yellowstar) i='⭐';;
|
||||
1F0CF|black_joker|joker|wild) i='🃏';;
|
||||
2705|white_check_mark|check) i='✅';;
|
||||
274C|x|cross|xmark) i='❌';;
|
||||
1F6BD|toilet|restroom|loo) i='🚽';;
|
||||
1F514|bell|ringer|ring) i='🔔';;
|
||||
1F50E|mag_right|search|magnify) i='🔎';;
|
||||
1F3AF|dart|bullseye|darts) i='🎯';;
|
||||
1F4B5|dollar|cash|cream) i='💵';;
|
||||
1F4AD|thought_balloon|thinking) i='💭';;
|
||||
1F340|four_leaf_clover|luck) i='🍀';;
|
||||
|
||||
*)
|
||||
#entity $1; return 0;;
|
||||
esac
|
||||
echo -ne "$i"
|
||||
}
|
||||
|
||||
function e {
|
||||
emoji "$@"
|
||||
}
|
||||
|
||||
#SHML:END
|
||||
|
||||
|
||||
# Usage / Examples
|
||||
##
|
||||
if [[ "$(basename -- "$0")" = "shml.sh" ]]; then
|
||||
I=2
|
||||
echo -e "
|
||||
|
||||
$(a bold 'SHML Usage / Help')
|
||||
$(hr '=')
|
||||
|
||||
$(a bold 'Section 0: Sourcing')
|
||||
$(hr '-')
|
||||
|
||||
$(i $I)When installed in path:
|
||||
$(i $I) source \$(which shml.sh)
|
||||
|
||||
$(i $I)When installed locally:
|
||||
$(i $I) source ./shml.sh
|
||||
|
||||
$(a bold 'Section 1: Foreground')
|
||||
$(hr '-')
|
||||
|
||||
$(i $I)\$(color red \"foo bar\")
|
||||
$(i $I)$(color red "foo bar")
|
||||
|
||||
$(i $I)\$(color blue \"foo bar\")
|
||||
$(i $I)$(color blue "foo bar")
|
||||
|
||||
$(i $I)\$(fgcolor green)
|
||||
$(i $I) >>foo bar<<
|
||||
$(i $I) >>bah boo<<
|
||||
$(i $I)\$(fgcolor end)
|
||||
$(i $I)$(fgcolor green)
|
||||
$(i $I)>>foo bar<<
|
||||
$(i $I)>>bah boo<<
|
||||
$(i $I)$(fgcolor end)
|
||||
|
||||
$(i $I)Short Hand: $(a underline 'c')
|
||||
|
||||
$(i $I)\$(c red 'foo')
|
||||
|
||||
$(i $I)Argument list:
|
||||
|
||||
$(i $I)black, red, green, yellow, blue, magenta, cyan, gray,
|
||||
$(i $I)white, darkgray, lightgreen, lightyellow, lightblue,
|
||||
$(i $I)lightmagenta, lightcyan
|
||||
|
||||
$(i $I)Termination: end, off, reset
|
||||
|
||||
$(i $I)Default (no arg): end
|
||||
|
||||
|
||||
$(a bold 'Section 2: Background')
|
||||
$(hr '-')
|
||||
|
||||
$(i $I)\$(bgcolor red \"foo bar\")
|
||||
$(i $I)$(background red "foo bar")
|
||||
|
||||
$(i $I)\$(background blue \"foo bar\")
|
||||
$(i $I)$(background blue "foo bar")
|
||||
|
||||
$(i $I)\$(background green)
|
||||
$(i $I)$(i $I)>>foo bar<<
|
||||
$(i $I)$(i $I)>>bah boo<<
|
||||
$(i $I)\$(background end)
|
||||
$(background green)
|
||||
$(i $I)>>foo bar<<
|
||||
$(i $I)>>bah boo<<
|
||||
$(background end)
|
||||
|
||||
$(i $I)Short Hand: $(a underline 'bg')
|
||||
|
||||
$(i $I)\$(bg red 'foo')
|
||||
|
||||
$(i $I)Argument list:
|
||||
|
||||
$(i $I)black, red, green, yellow, blue, magenta, cyan, gray,
|
||||
$(i $I)white, darkgray, lightred, lightgreen, lightyellow,
|
||||
$(i $I)lightblue, lightmagenta, lightcyan
|
||||
|
||||
$(i $I)Termination: end, off, reset
|
||||
|
||||
$(i $I)Default (no arg): end
|
||||
|
||||
|
||||
$(a bold 'Section 3: Attributes')
|
||||
$(hr '-')
|
||||
|
||||
$(i $I)$(a bold "Attributes only work on vt100 compatible terminals.")
|
||||
|
||||
$(i $I)> Note:
|
||||
$(i $I)> $(a underline 'attribute end') turns off everything,
|
||||
$(i $I)> including foreground and background color.
|
||||
|
||||
$(i $I)\$(attribute bold \"foo bar\")
|
||||
$(i $I)$(attribute bold "foo bar")
|
||||
|
||||
$(i $I)\$(attribute underline \"foo bar\")
|
||||
$(i $I)$(attribute underline "foo bar")
|
||||
|
||||
$(i $I)\$(attribute blink \"foo bar\")
|
||||
$(i $I)$(attribute blink "foo bar")
|
||||
|
||||
$(i $I)\$(attribute invert \"foo bar\")
|
||||
$(i $I)$(attribute invert "foo bar")
|
||||
|
||||
$(i $I)\$(attribute dim)
|
||||
$(i $I)$(i $I)>>foo bar<<
|
||||
$(i $I)$(i $I)>>bah boo<<
|
||||
$(i $I)\$(attribute end)
|
||||
$(i $I)$(attribute dim)
|
||||
$(i $I)$(i $I)>>foo bar<<
|
||||
$(i $I)$(i $I)>>bah boo<<
|
||||
$(i $I)$(attribute end)
|
||||
|
||||
$(i $I)Short Hand: $(a underline 'a')
|
||||
|
||||
$(i $I)\$(a bold 'foo')
|
||||
|
||||
$(i $I)Argument list:
|
||||
|
||||
$(i $I)bold, dim, underline, blink, invert, hidden
|
||||
|
||||
$(i $I)Termination: end, off, reset
|
||||
|
||||
$(i $I)Default (no arg): end
|
||||
|
||||
|
||||
$(a bold 'Section 4: Elements')
|
||||
$(hr '-')
|
||||
|
||||
$(i $I)foo\$(br)\$(tab)bar
|
||||
$(i $I)foo$(br)$(tab)bar
|
||||
$(i $I)
|
||||
$(i $I)foo\$(br)\$(indent)bar\$(br)\$(indent 6)boo
|
||||
$(i $I)foo$(br)$(indent)bar$(br)$(indent 6)boo
|
||||
$(i $I)
|
||||
$(i $I)> Note: short hand for $(a underline 'indent') is $(a underline 'i')
|
||||
$(i $I)
|
||||
$(i $I)\$(hr)
|
||||
$(i $I)$(hr)
|
||||
$(i $I)
|
||||
$(i $I)\$(hr 50)
|
||||
$(i $I)$(hr 50)
|
||||
$(i $I)
|
||||
$(i $I)\$(hr '~' 40)
|
||||
$(i $I)$(hr '~' 40)
|
||||
$(i $I)
|
||||
$(i $I)\$(hr '#' 30)
|
||||
$(i $I)$(hr '#' 30)
|
||||
|
||||
|
||||
$(a bold 'Section 5: Icons')
|
||||
$(hr '-')
|
||||
|
||||
$(i $I)Icons
|
||||
$(i $I)$(hr '-' 10)
|
||||
|
||||
$(i $I)\$(icon check) \$(icon '<3') \$(icon '*') \$(icon ':)')
|
||||
|
||||
$(i $I)$(icon check) $(icon '<3') $(icon '*') $(icon 'smile')
|
||||
|
||||
$(i $I)Argument list:
|
||||
|
||||
$(i $I)check|checkmark, X|x|xmark, <3|heart, sun, *|star,
|
||||
$(i $I)darkstar, umbrella, flag, snow|snowflake, music,
|
||||
$(i $I)scissors, tm|trademark, copyright, apple,
|
||||
$(i $I):-)|:)|smile|face
|
||||
|
||||
|
||||
$(a bold 'Section 6: Emojis')
|
||||
$(hr '-')
|
||||
|
||||
$(i $I)Couldn't peep it with a pair of \$(emoji bifocals)
|
||||
$(i $I)Couldn't peep it with a pair of $(emoji bifocals)
|
||||
$(i $I)
|
||||
$(i $I)I'm no \$(emoji joker) play me as a \$(emoji joker)
|
||||
$(i $I)I'm no $(emoji joker) play me as a $(emoji joker)
|
||||
$(i $I)
|
||||
$(i $I)\$(emoji bee) on you like a \$(emoji house) on \$(emoji fire), \$(emoji smoke) ya
|
||||
$(i $I)$(emoji bee) on you like a $(emoji house) on $(emoji fire), $(emoji smoke) ya
|
||||
$(i $I)
|
||||
$(i $I)$(a bold 'Each Emoji has 1 or more alias')
|
||||
$(i $I)
|
||||
$(i $I)\$(emoji smiley) \$(emoji 1F603) \$(emoji '=)') \$(emoji ':-)') \$(emoji ':)')
|
||||
$(i $I)$(emoji smiley) $(emoji 1F603) $(emoji '=)') $(emoji ':-)') $(emoji ':)')
|
||||
|
||||
$(a bold 'Section 7: Color Bar')
|
||||
$(hr '-')
|
||||
|
||||
$(i $I)\$(color-bar)
|
||||
$(i $I)$(color-bar)
|
||||
$(i $I)
|
||||
$(i $I)\$(color-bar red green yellow blue magenta \\
|
||||
$(i $I)$(i 15)cyan lightgray darkgray lightred \\
|
||||
$(i $I)$(i 15)lightgreen lightyellow lightblue \\
|
||||
$(i $I)$(i 15)lightmagenta lightcyan)
|
||||
$(i $I)$(color-bar red green yellow blue magenta \
|
||||
cyan lightgray darkgray lightred \
|
||||
lightgreen lightyellow lightblue \
|
||||
lightmagenta lightcyan)
|
||||
|
||||
$(i $I)Short Hand: $(a underline 'bar')
|
||||
$(i $I)
|
||||
$(i $I)\$(bar black yellow black yellow black yellow)
|
||||
$(i $I)$(bar black yellow black yellow black yellow)
|
||||
|
||||
" | less -r
|
||||
fi
|
||||
|
||||
# vim: ft=sh:
|
|
@ -14,3 +14,7 @@ export EDITOR='nano'
|
|||
fpath+=(${DOTF_LIB}/completions/src ${DOTF_LIB}/local)
|
||||
|
||||
for file in ${ZSHRCD}/*.zsh; do source $file; done
|
||||
|
||||
# Prompt
|
||||
autoload -U promptinit && promptinit
|
||||
prompt filthy
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
export ZSH="${DOTF_LIB}/ohmyzsh"
|
||||
|
||||
# Settings
|
||||
ZSH_THEME="mortalscumbag"
|
||||
#ZSH_THEME="mortalscumbag"
|
||||
DISABLE_AUTO_UPDATE="true"
|
||||
|
||||
source ${ZSH}/oh-my-zsh.sh
|
||||
|
|
34
lib/local/_desk
Normal file
34
lib/local/_desk
Normal file
|
@ -0,0 +1,34 @@
|
|||
#compdef desk
|
||||
#autoload
|
||||
|
||||
_all_desks() {
|
||||
desks=($(desk list --only-names))
|
||||
}
|
||||
|
||||
local expl
|
||||
local -a desks
|
||||
|
||||
local -a _subcommands
|
||||
_subcommands=(
|
||||
'help:Print a help message.'
|
||||
'init:Initialize your desk configuration.'
|
||||
'list:List available desks'
|
||||
'ls:List available desks'
|
||||
'edit:Edit or create a desk, defaults to current desk'
|
||||
'go:Activate a desk'
|
||||
'.:Activate a desk'
|
||||
'run:Run a command within a desk environment'
|
||||
'version:Show the desk version.'
|
||||
)
|
||||
|
||||
if (( CURRENT == 2 )); then
|
||||
_describe -t commands 'desk subcommand' _subcommands
|
||||
return
|
||||
fi
|
||||
|
||||
case "$words[2]" in
|
||||
go|.|edit|run)
|
||||
_all_desks
|
||||
_wanted desks expl 'desks' compadd -a desks ;;
|
||||
esac
|
||||
|
61
lib/local/_lj
Normal file
61
lib/local/_lj
Normal file
|
@ -0,0 +1,61 @@
|
|||
#compdef lj
|
||||
|
||||
###
|
||||
# List of log levels
|
||||
###
|
||||
_levels=(
|
||||
'emergency'
|
||||
'alert'
|
||||
'critical'
|
||||
'error'
|
||||
'warning'
|
||||
'notice'
|
||||
'info'
|
||||
'debug'
|
||||
)
|
||||
|
||||
###
|
||||
# Describe log levels
|
||||
###
|
||||
function _lumberjack_log_levels() {
|
||||
_describe -t levels 'levels' _levels "$@"
|
||||
}
|
||||
|
||||
###
|
||||
# Lumberjack completion
|
||||
###
|
||||
function _lumberjack() {
|
||||
typeset -A opt_args
|
||||
local context state line curcontext="$curcontext"
|
||||
|
||||
# Set option arguments
|
||||
_arguments -A \
|
||||
'(-h --help)'{-h,--help}'[show help text and exit]' \
|
||||
'(-v --version)'{-v,--version}'[show version information and exit]' \
|
||||
'(-f --file)'{-f,--file}'[set log file and exit]' \
|
||||
'(-l --level)'{-l,--level}'[set log level and exit]' \
|
||||
|
||||
# Set log level arguments
|
||||
_arguments \
|
||||
'1: :_lumberjack_log_levels' \
|
||||
'*::arg:->args'
|
||||
|
||||
# Complete option arguments
|
||||
case "$state" in
|
||||
args )
|
||||
case "$words[1]" in
|
||||
--file|-f )
|
||||
_arguments \
|
||||
'1:file:_files'
|
||||
;;
|
||||
--level|-l )
|
||||
_arguments \
|
||||
'1:level:_lumberjack_log_levels'
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_lumberjack "$@"
|
||||
|
312
lib/local/prompt_filthy_setup
Normal file
312
lib/local/prompt_filthy_setup
Normal file
|
@ -0,0 +1,312 @@
|
|||
#!/usr/bin/env zsh
|
||||
|
||||
# Filthy
|
||||
# by James Dinsdale
|
||||
# https://github.com/molovo/filthy
|
||||
# MIT License
|
||||
|
||||
# Largely based on Pure by Sindre Sorhus <https://github.com/sindresorhus/pure>
|
||||
|
||||
# For my own and others sanity
|
||||
# git:
|
||||
# %b => current branch
|
||||
# %a => current action (rebase/merge)
|
||||
# prompt:
|
||||
# %F => color dict
|
||||
# %f => reset color
|
||||
# %~ => current path
|
||||
# %* => time
|
||||
# %n => username
|
||||
# %m => shortname host
|
||||
# %(?..) => prompt conditional - %(condition.true.false)
|
||||
|
||||
prompt_filthy_nice_exit_code() {
|
||||
local exit_status="${1:-$(print -P %?)}";
|
||||
# nothing to do here
|
||||
[[ ${FILTHY_SHOW_EXIT_CODE:=0} != 1 || -z $exit_status || $exit_status == 0 ]] && return;
|
||||
|
||||
local sig_name;
|
||||
|
||||
# is this a signal name (error code = signal + 128) ?
|
||||
case $exit_status in
|
||||
129) sig_name=HUP ;;
|
||||
130) sig_name=INT ;;
|
||||
131) sig_name=QUIT ;;
|
||||
132) sig_name=ILL ;;
|
||||
134) sig_name=ABRT ;;
|
||||
136) sig_name=FPE ;;
|
||||
137) sig_name=KILL ;;
|
||||
139) sig_name=SEGV ;;
|
||||
141) sig_name=PIPE ;;
|
||||
143) sig_name=TERM ;;
|
||||
esac
|
||||
|
||||
# usual exit codes
|
||||
case $exit_status in
|
||||
-1) sig_name=FATAL ;;
|
||||
1) sig_name=WARN ;; # Miscellaneous errors, such as "divide by zero"
|
||||
2) sig_name=BUILTINMISUSE ;; # misuse of shell builtins (pretty rare)
|
||||
126) sig_name=CCANNOTINVOKE ;; # cannot invoke requested command (ex : source script_with_syntax_error)
|
||||
127) sig_name=CNOTFOUND ;; # command not found (ex : source script_not_existing)
|
||||
esac
|
||||
|
||||
# assuming we are on an x86 system here
|
||||
# this MIGHT get annoying since those are in a range of exit codes
|
||||
# programs sometimes use.... we'll see.
|
||||
case $exit_status in
|
||||
19) sig_name=STOP ;;
|
||||
20) sig_name=TSTP ;;
|
||||
21) sig_name=TTIN ;;
|
||||
22) sig_name=TTOU ;;
|
||||
esac
|
||||
|
||||
echo "$ZSH_PROMPT_EXIT_SIGNAL_PREFIX${exit_status}:${sig_name:-$exit_status}$ZSH_PROMPT_EXIT_SIGNAL_SUFFIX ";
|
||||
}
|
||||
|
||||
# turns seconds into human readable time
|
||||
# 165392 => 1d 21h 56m 32s
|
||||
prompt_filthy_human_time() {
|
||||
local tmp=$(( $1 / 1000 ))
|
||||
local days=$(( tmp / 60 / 60 / 24 ))
|
||||
local hours=$(( tmp / 60 / 60 % 24 ))
|
||||
local minutes=$(( tmp / 60 % 60 ))
|
||||
local seconds=$(( tmp % 60 ))
|
||||
(( $days > 0 )) && print -n "${days}d "
|
||||
(( $hours > 0 )) && print -n "${hours}h "
|
||||
(( $minutes > 0 )) && print -n "${minutes}m "
|
||||
(( $seconds > 5 )) && print -n "${seconds}s"
|
||||
(( $tmp <= 5 )) && print -n "${1}ms"
|
||||
}
|
||||
|
||||
# displays the exec time of the last command if set threshold was exceeded
|
||||
prompt_filthy_cmd_exec_time() {
|
||||
local stop=$((EPOCHREALTIME*1000))
|
||||
local start=${cmd_timestamp:-$stop}
|
||||
integer elapsed=$stop-$start
|
||||
(($elapsed > ${FILTHY_CMD_MAX_EXEC_TIME:=500})) && prompt_filthy_human_time $elapsed
|
||||
}
|
||||
|
||||
prompt_filthy_preexec() {
|
||||
cmd_timestamp=$((EPOCHREALTIME*1000))
|
||||
|
||||
# shows the current dir and executed command in the title when a process is active
|
||||
print -Pn "\e]0;"
|
||||
echo -nE "$PWD:t: $2"
|
||||
print -Pn "\a"
|
||||
}
|
||||
|
||||
# string length ignoring ansi escapes
|
||||
prompt_filthy_string_length() {
|
||||
print ${#${(S%%)1//(\%([KF1]|)\{*\}|\%[Bbkf])}}
|
||||
}
|
||||
|
||||
prompt_filthy_precmd() {
|
||||
local prompt_filthy_preprompt git_root current_path branch repo_status
|
||||
|
||||
# Ensure prompt starts on a new line
|
||||
prompt_filthy_preprompt="\n"
|
||||
|
||||
# Print connection info
|
||||
prompt_filthy_preprompt+="$(prompt_filthy_connection_info)"
|
||||
|
||||
# check if we're in a git repo, and show git info if we are
|
||||
if command git rev-parse --is-inside-work-tree &>/dev/null; then
|
||||
# Print the name of the repository
|
||||
git_root=$(git rev-parse --show-toplevel)
|
||||
prompt_filthy_preprompt+="%B%F{yellow}$(basename ${git_root})%b%f"
|
||||
|
||||
# Print the current_path relative to the git root
|
||||
current_path=$(git rev-parse --show-prefix)
|
||||
prompt_filthy_preprompt+=" %F{blue}${${current_path%/}:-"/"}%f"
|
||||
else
|
||||
# We're not in a repository, so just print the current path
|
||||
prompt_filthy_preprompt+="%F{blue}%~%f"
|
||||
fi
|
||||
|
||||
# Print everything so far in the title
|
||||
# print -Pn '\e]0;${prompt_filthy_preprompt}\a'
|
||||
|
||||
# Echo command exec time
|
||||
prompt_filthy_preprompt+=" %F{yellow}$(prompt_filthy_cmd_exec_time)%f"
|
||||
|
||||
if [[ -f "${ZDOTDIR:-$HOME}/.promptmsg" ]]; then
|
||||
# Echo any stored messages after the pre-prompt
|
||||
prompt_filthy_preprompt+=" $(cat ${ZDOTDIR:-$HOME}/.promptmsg)"
|
||||
fi
|
||||
|
||||
# We've already added any messages to our prompt, so let's reset them
|
||||
cat /dev/null >! "${ZDOTDIR:-$HOME}/.promptmsg"
|
||||
|
||||
print -P $prompt_filthy_preprompt
|
||||
|
||||
# reset value since `preexec` isn't always triggered
|
||||
unset cmd_timestamp
|
||||
}
|
||||
|
||||
prompt_filthy_rprompt() {
|
||||
# check if we're in a git repo, and show git info if we are
|
||||
if command git rev-parse --is-inside-work-tree &>/dev/null; then
|
||||
# Print the repository status
|
||||
branch=$(prompt_filthy_git_branch)
|
||||
repo_status=$(prompt_filthy_git_repo_status)
|
||||
ci_status=$(prompt_filthy_ci_status)
|
||||
fi
|
||||
|
||||
if builtin type zvm >/dev/null 2>&1; then
|
||||
zvm_version=" $(zvm current --quiet)"
|
||||
fi
|
||||
|
||||
print "${branch}${repo_status}${ci_status}%F{yellow}${zvm_version}%f"
|
||||
}
|
||||
|
||||
prompt_filthy_ci_status() {
|
||||
local state git_dir_local state_file
|
||||
|
||||
[[ $FILTHY_SHOW_CI_STATUS -eq 0 ]] && return
|
||||
|
||||
builtin type hub >/dev/null 2>&1 || return
|
||||
|
||||
git_dir_local="$(git rev-parse --git-dir)"
|
||||
state_file="${git_dir_local}/ci-status"
|
||||
|
||||
function _retrieve_ci_status() {
|
||||
# Delay the asynchronous process, otherwise the status file
|
||||
# will be empty when we read it
|
||||
sleep 1
|
||||
|
||||
state=$(hub ci-status 2>&1)
|
||||
cat /dev/null >! "${state_file}" 2>/dev/null
|
||||
case $state in
|
||||
success )
|
||||
print '%F{green}●%f' >> "${state_file}"
|
||||
;;
|
||||
pending )
|
||||
print '%F{yellow}○%f' >> "${state_file}"
|
||||
;;
|
||||
failure )
|
||||
print '%F{red}●%f' >> "${state_file}"
|
||||
;;
|
||||
error )
|
||||
print '%F{red}‼%f' >> "${state_file}"
|
||||
;;
|
||||
'no status' )
|
||||
print '%F{242}○%f' >> "${state_file}"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_retrieve_ci_status >/dev/null 2>&1 &!
|
||||
|
||||
state=$(cat "${state_file}" 2>/dev/null)
|
||||
|
||||
[[ -n $state ]] && print " $state"
|
||||
}
|
||||
|
||||
prompt_filthy_git_repo_status() {
|
||||
# Do a fetch asynchronously
|
||||
git fetch > /dev/null 2>&1 &!
|
||||
|
||||
local clean
|
||||
local rtn=""
|
||||
local count
|
||||
local up
|
||||
local down
|
||||
|
||||
dirty="$(git diff --ignore-submodules=all HEAD 2>/dev/null)"
|
||||
[[ $dirty != "" ]] && rtn+=" %F{242}…%f"
|
||||
|
||||
staged="$(git diff --staged HEAD 2>/dev/null)"
|
||||
[[ $staged != "" ]] && rtn+=" %F{242}*%f"
|
||||
|
||||
# check if there is an upstream configured for this branch
|
||||
# exit if there isn't, as we can't check for remote changes
|
||||
if command git rev-parse --abbrev-ref @'{u}' &>/dev/null; then
|
||||
# if there is, check git left and right arrow_status
|
||||
count="$(command git rev-list --left-right --count HEAD...@'{u}' 2>/dev/null)"
|
||||
|
||||
# Get the push and pull counts
|
||||
up="$count[(w)1]"
|
||||
down="$count[(w)2]"
|
||||
|
||||
# Check if either push or pull is needed
|
||||
[[ $up > 0 || $down > 0 ]] && rtn+=" "
|
||||
|
||||
# Push is needed, show up arrow
|
||||
[[ $up > 0 ]] && rtn+="%F{yellow}⇡%f"
|
||||
|
||||
# Pull is needed, show down arrow
|
||||
[[ $down > 0 ]] && rtn+="%F{yellow}⇣%f"
|
||||
fi
|
||||
|
||||
print $rtn
|
||||
}
|
||||
|
||||
prompt_filthy_git_branch() {
|
||||
# get the current git status
|
||||
local branch git_dir_local rtn
|
||||
|
||||
branch=$(git status --short --branch -uno --ignore-submodules=all | head -1 | awk '{print $2}' 2>/dev/null)
|
||||
git_dir_local=$(git rev-parse --git-dir)
|
||||
|
||||
# remove reference to any remote tracking branch
|
||||
branch=${branch%...*}
|
||||
|
||||
# check if HEAD is detached
|
||||
if [[ -d "${git_dir_local}/rebase-merge" ]]; then
|
||||
branch=$(git status | head -5 | tail -1 | awk '{print $6}')
|
||||
rtn="%F{red}rebasing interactively%f%F{242} → ${branch//([[:space:]]|\')/}%f"
|
||||
elif [[ -d "${git_dir_local}/rebase-apply" ]]; then
|
||||
branch=$(git status | head -2 | tail -1 | awk '{print $6}')
|
||||
rtn="%F{red}rebasing%f%F{242} → ${branch//([[:space:]]|\')/}%f"
|
||||
elif [[ -f "${git_dir_local}/MERGE_HEAD" ]]; then
|
||||
branch=$(git status | head -1 | awk '{print $3}')
|
||||
rtn="%F{red}merging%f%F{242} → ${branch//([[:space:]]|\')/}%f"
|
||||
elif [[ "$branch" = "HEAD" ]]; then
|
||||
commit=$(git status HEAD -uno --ignore-submodules=all | head -1 | awk '{print $4}' 2>/dev/null)
|
||||
|
||||
if [[ "$commit" = "on" ]]; then
|
||||
rtn="%F{yellow}no branch%f"
|
||||
else
|
||||
rtn="%F{242}detached@%f"
|
||||
rtn+="%F{yellow}"
|
||||
rtn+="$commit"
|
||||
rtn+="%f"
|
||||
fi
|
||||
else
|
||||
rtn="%F{242}$branch%f"
|
||||
fi
|
||||
|
||||
print "$rtn"
|
||||
}
|
||||
|
||||
prompt_filthy_connection_info() {
|
||||
# show username@host if logged in through SSH
|
||||
if [[ "x$SSH_CONNECTION" != "x" ]]; then
|
||||
echo '%(!.%B%F{red}%n%f%b.%F{242}%n%f)%F{242}@%m%f '
|
||||
else
|
||||
echo '%(!.%B%F{red}%n%f%b%F{242}@%m%f .)'
|
||||
fi
|
||||
}
|
||||
|
||||
prompt_filthy_setup() {
|
||||
# prevent percentage showing up
|
||||
# if output doesn't end with a newline
|
||||
export PROMPT_EOL_MARK=''
|
||||
|
||||
prompt_opts=(cr subst percent)
|
||||
|
||||
zmodload zsh/datetime
|
||||
autoload -Uz add-zsh-hook
|
||||
# autoload -Uz git-info
|
||||
|
||||
add-zsh-hook precmd prompt_filthy_precmd
|
||||
add-zsh-hook preexec prompt_filthy_preexec
|
||||
|
||||
# prompt turns red if the previous command didn't exit with 0
|
||||
PROMPT='%(?.%F{green}.%F{red}$(prompt_filthy_nice_exit_code))❯%f '
|
||||
|
||||
RPROMPT='$(prompt_filthy_rprompt)'
|
||||
}
|
||||
|
||||
prompt_filthy_setup "$@"
|
||||
|
Loading…
Add table
Reference in a new issue