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

mdfmt.sh 6.1KB

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