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)
|
fpath+=(${DOTF_LIB}/completions/src ${DOTF_LIB}/local)
|
||||||
|
|
||||||
for file in ${ZSHRCD}/*.zsh; do source $file; done
|
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"
|
export ZSH="${DOTF_LIB}/ohmyzsh"
|
||||||
|
|
||||||
# Settings
|
# Settings
|
||||||
ZSH_THEME="mortalscumbag"
|
#ZSH_THEME="mortalscumbag"
|
||||||
DISABLE_AUTO_UPDATE="true"
|
DISABLE_AUTO_UPDATE="true"
|
||||||
|
|
||||||
source ${ZSH}/oh-my-zsh.sh
|
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