#!/bin/sh #shellcheck disable=SC2039 # # mdfmt - Markdown formatted output from your shell scripts # # These functions allow you to easily output a valid Markdown # from your script. # # # Default text width # # Most paragraph text will be re-formatted to not exceed this width. # MDFMT_WIDTH=${MDFMT_WIDTH:-75} _mdfmt__fmttype() { # # Format fragments $@ as type $Elem # case $Elem in h1) local hdr hdr=$(_mdfmt__join "$@") echo echo echo "$hdr" echo "$hdr" | tr -c '\n' = ;; h2) local hdr hdr=$(_mdfmt__join "$@") echo echo echo "$hdr" echo "$hdr" | tr -c '\n' - ;; h3) local hdr hdr=$(_mdfmt__join "$@") echo echo echo "### $hdr ###" ;; p) echo _mdfmt__join "$@" \ | fmt -w "$(_mdfmt__width)" ;; ul) printf '\n * ' _mdfmt__join "$@" \ | fmt -t -w "$(_mdfmt__width)" \ | sed -e 's/^ / /' ;; ol) printf '\n 1. ' _mdfmt__join "$@" \ | fmt -t -w "$(_mdfmt__width)" \ | sed -e 's/^ / /' ;; pre) echo _mdfmt__join "$@" \ | sed -e 's/^/ /' ;; esac } _mdfmt__indent() { # # Indent as many spaces as needed # local pfx test "$Nest" -eq 0 && { cat; return; } test "$Elem" == h1 && { cat; return; } test "$Elem" == h2 && { cat; return; } test "$Elem" == h3 && { cat; return; } pfx=$( #shellcheck disable=SC2034 for n in $(seq 1 "$Nest"); do printf ' ' done ) sed "s/^/$pfx/" } _mdfmt__join() { # # Properly join text fragments $@ # case $Elem in pre) printf '%s\n' "$@" ;; h*) echo "$1" ;; *) echo "$@" ;; esac } _mdfmt__quote() { # # Quote if needed # local pfx $Quoted || { cat; return; } test "$Elem" == h1 && { cat; return; } test "$Elem" == h2 && { cat; return; } test "$Elem" == h3 && { cat; return; } sed 's/^/> /' } _mdfmt__width() { # # Calculate width # echo "$((MDFMT_WIDTH - Nest * 4))" } mdfmt() { # # Format text as Markdown element $1 # # Usage: # mdfmt [-n|--nest LEVEL] [-q|--quote] ELEM FRAG [FRAG..] # # Take text passed as one or more FRAGs and output it formatted in # Markdown as ELEM. Passing higher LEVEL than previous item causes # items to nest. # # When multiple FRAGs are provided, the text is simply joined together # (and in most cases re-formatted to fit $MDFMT_WIDTH). This allows # you to pass long texts and keep your code tidy by breaking the text # into several pieces and using line continuation character `\`. # # Additional character-level formatting is achieved using standard # Markdown motation, i.e. asterisk for emphasis, double asterisk # for strong emphasis and backtick for verbatim text. # # Options: # # * `-n LEVEL`, `--nest LEVEL` Causes item to be indented so that # it will be nested into the previous item, if the LEVEL is higher # than the previous one. Default value is zero. # # This can be used to e.g. include unordered items or code examples # inside ordered items, or multiple paragraphs per (un)ordered # item. # # * `-q`, `--quote` Causes item to appear blockquoted. # # Following values are available for ELEM (names are inspired by # HTML elements): # # * `h1`, `h2`, `h3` - headings of respective weight. Ignores # nesting. Accepts only single FRAG. # # * `p` - a paragraph. # # * `ul`, `ol` - unordered list item (bullet) or ordered (numbered) # list item. # # * `pre` - pre-formatted code. Multiple FRAGs are treated as lines # and spaces are preserved. # # Examples: # # mdfmt h1 "how to boil an egg" # # mdfmt p "this tutorial shows you how to boil an egg" \ # "the best way possible" # # mdfmt h2 "variant 1" # # mdfmt ol "take egg" # mdfmt ol "put it in a pot full of water" # mdfmt ol "place pot onto stove" # mdfmt ol "turn on stove" # mdfmt ol "wait" # mdfmt --nest 1 ul "for hard-boiled, wait 10 minutes" # mdfmt --nest 1 ul "for soft-boiled, wait 3 minutes" # mdfmt ol "turn off the stove" # mdfmt ol "replace water with cold water " # mdfmt ol "break and replace shell from the egg" # # mdfmt h2 "variant 1" # # mdfmt ol "start your favorite browser" # mdfmt ol "open youtube:" # mdfmt --nest 1 pre "https://www.youtube.com/" # mdfmt ol "enter following text in Search box:" # mdfmt --nest 1 pre "how to boil an egg" # mdfmt ol "watch at least 5 videos" # mdfmt ol "go to the closest restaurant and ask for" \ # "a boiled egg" # # mdfmt h2 "conclusion" # # mdfmt p "We hope you enjoy the egg. Seriously." # local Nest=0 local Elem local Quoted=false local usage="usage: mdfmt [-n|--nest LVL] ELEM FRAG [FRAG..]" while true; do case $1 in -n|--nest) Nest=$2 shift 2 || { echo "$usage" >&2 return 2 } ;; -q|--quote) Quoted=true shift ;; -*) echo "$usage" >&2 return 2 ;; *) break ;; esac done Elem=$1; shift case $Elem in h1|h2|h3|p|ol|ul|pre) true ;; *) echo "$usage" >&2 return 2 ;; esac _mdfmt__fmttype "$@" \ | _mdfmt__quote \ | _mdfmt__indent }