shell dot on steroids https://pagure.io/shellfu

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. #!/bin/sh
  2. #shellcheck disable=SC2039
  3. #
  4. # mdfmt - Markdown formatted output from your shell scripts
  5. #
  6. # These functions allow you to easily output a valid Markdown
  7. # from your script.
  8. #
  9. #
  10. # Default text width
  11. #
  12. # Most paragraph text will be re-formatted to not exceed this width.
  13. #
  14. MDFMT_WIDTH=${MDFMT_WIDTH:-75}
  15. _mdfmt__fmttype() {
  16. #
  17. # Format fragments $@ as type $Elem
  18. #
  19. case $Elem in
  20. h1)
  21. local hdr
  22. hdr=$(_mdfmt__join "$@")
  23. echo
  24. echo
  25. echo "$hdr"
  26. echo "$hdr" | tr -c '\n' =
  27. ;;
  28. h2)
  29. local hdr
  30. hdr=$(_mdfmt__join "$@")
  31. echo
  32. echo
  33. echo "$hdr"
  34. echo "$hdr" | tr -c '\n' -
  35. ;;
  36. h3)
  37. local hdr
  38. hdr=$(_mdfmt__join "$@")
  39. echo
  40. echo
  41. echo "### $hdr ###"
  42. ;;
  43. p)
  44. echo
  45. _mdfmt__join "$@" \
  46. | fmt -w "$(_mdfmt__width)"
  47. ;;
  48. ul)
  49. printf '\n * '
  50. _mdfmt__join "$@" \
  51. | fmt -t -w "$(_mdfmt__width)" \
  52. | sed -e 's/^ / /'
  53. ;;
  54. ol)
  55. printf '\n 1. '
  56. _mdfmt__join "$@" \
  57. | fmt -t -w "$(_mdfmt__width)" \
  58. | sed -e 's/^ / /'
  59. ;;
  60. pre)
  61. echo
  62. _mdfmt__join "$@" \
  63. | sed -e 's/^/ /'
  64. ;;
  65. esac
  66. }
  67. _mdfmt__indent() {
  68. #
  69. # Indent as many spaces as needed
  70. #
  71. local pfx
  72. test "$Nest" -eq 0 && { cat; return; }
  73. test "$Elem" == h1 && { cat; return; }
  74. test "$Elem" == h2 && { cat; return; }
  75. test "$Elem" == h3 && { cat; return; }
  76. pfx=$(
  77. for n in $(seq 1 "$Nest"); do
  78. printf ' '
  79. done
  80. )
  81. sed "s/^/$pfx/"
  82. }
  83. _mdfmt__join() {
  84. #
  85. # Properly join text fragments $@
  86. #
  87. case $Elem in
  88. pre) printf '%s\n' "$@" ;;
  89. h*) echo "$1" ;;
  90. *) echo "$@" ;;
  91. esac
  92. }
  93. _mdfmt__quote() {
  94. #
  95. # Quote if needed
  96. #
  97. local pfx
  98. $Quoted || { cat; return; }
  99. test "$Elem" == h1 && { cat; return; }
  100. test "$Elem" == h2 && { cat; return; }
  101. test "$Elem" == h3 && { cat; return; }
  102. sed 's/^/> /'
  103. }
  104. _mdfmt__width() {
  105. #
  106. # Calculate width
  107. #
  108. echo "$((MDFMT_WIDTH - Nest * 4))"
  109. }
  110. mdfmt() {
  111. #
  112. # Format text as Markdown element $1
  113. #
  114. # Usage:
  115. # mdfmt [-n|--nest LEVEL] [-q|--quote] ELEM FRAG [FRAG..]
  116. #
  117. # Take text passed as one or more FRAGs and output it formatted in
  118. # Markdown as ELEM. Passing higher LEVEL than previous item causes
  119. # items to nest.
  120. #
  121. # When multiple FRAGs are provided, the text is simply joined together
  122. # (and in most cases re-formatted to fit $MDFMT_WIDTH). This allows
  123. # you to pass long texts and keep your code tidy by breaking the text
  124. # into several pieces and using line continuation character `\`.
  125. #
  126. # Additional character-level formatting is achieved using standard
  127. # Markdown motation, i.e. asterisk for emphasis, double asterisk
  128. # for strong emphasis and backtick for verbatim text.
  129. #
  130. # Options:
  131. #
  132. # * `-n LEVEL`, `--nest LEVEL` Causes item to be indented so that
  133. # it will be nested into the previous item, if the LEVEL is higher
  134. # than the previous one. Default value is zero.
  135. #
  136. # This can be used to e.g. include unordered items or code examples
  137. # inside ordered items, or multiple paragraphs per (un)ordered
  138. # item.
  139. #
  140. # * `-q`, `--quote` Causes item to appear blockquoted.
  141. #
  142. # Following values are available for ELEM (names are inspired by
  143. # HTML elements):
  144. #
  145. # * `h1`, `h2`, `h3` - headings of respective weight. Ignores
  146. # nesting. Accepts only single FRAG.
  147. #
  148. # * `p` - a paragraph.
  149. #
  150. # * `ul`, `ol` - unordered list item (bullet) or ordered (numbered)
  151. # list item.
  152. #
  153. # * `pre` - pre-formatted code. Multiple FRAGs are treated as lines
  154. # and spaces are preserved.
  155. #
  156. # Examples:
  157. #
  158. # mdfmt h1 "how to boil an egg"
  159. #
  160. # mdfmt p "this tutorial shows you how to boil an egg" \
  161. # "the best way possible"
  162. #
  163. # mdfmt h2 "variant 1"
  164. #
  165. # mdfmt ol "take egg"
  166. # mdfmt ol "put it in a pot full of water"
  167. # mdfmt ol "place pot onto stove"
  168. # mdfmt ol "turn on stove"
  169. # mdfmt ol "wait"
  170. # mdfmt --nest 1 ul "for hard-boiled, wait 10 minutes"
  171. # mdfmt --nest 1 ul "for soft-boiled, wait 3 minutes"
  172. # mdfmt ol "turn off the stove"
  173. # mdfmt ol "replace water with cold water "
  174. # mdfmt ol "break and replace shell from the egg"
  175. #
  176. # mdfmt h2 "variant 1"
  177. #
  178. # mdfmt ol "start your favorite browser"
  179. # mdfmt ol "open youtube:"
  180. # mdfmt --nest 1 pre "https://www.youtube.com/"
  181. # mdfmt ol "enter following text in Search box:"
  182. # mdfmt --nest 1 pre "how to boil an egg"
  183. # mdfmt ol "watch at least 5 videos"
  184. # mdfmt ol "go to the closest restaurant and ask for" \
  185. # "a boiled egg"
  186. #
  187. # mdfmt h2 "conclusion"
  188. #
  189. # mdfmt p "We hope you enjoy the egg. Seriously."
  190. #
  191. local Nest=0
  192. local Elem
  193. local Quoted=false
  194. local usage="usage: mdfmt [-n|--nest LVL] ELEM FRAG [FRAG..]"
  195. while true; do case $1 in
  196. -n|--nest)
  197. Nest=$2
  198. shift 2 || {
  199. echo "$usage" >&2
  200. return 2
  201. }
  202. ;;
  203. -q|--quote)
  204. Quoted=true
  205. shift
  206. ;;
  207. -*)
  208. echo "$usage" >&2
  209. return 2
  210. ;;
  211. *)
  212. break
  213. ;;
  214. esac done
  215. Elem=$1; shift
  216. case $Elem in
  217. h1|h2|h3|p|ol|ul|pre)
  218. true
  219. ;;
  220. *)
  221. echo "$usage" >&2
  222. return 2
  223. ;;
  224. esac
  225. _mdfmt__fmttype "$@" \
  226. | _mdfmt__quote \
  227. | _mdfmt__indent
  228. }