#!/bin/bash
# doc/rst - reStructuredText file support
# The rst library provides support for reStructuredText file generation.
# The following document was referenced often during implementation:
#
# http://http://docutils.sourceforge.net/rst.html
set -e
######
# Native dependencies
doc_rst_client_packages=( python-docutils )
######
# Configuration
doc_rst_config_init() {
lib_setting_vars --null rst_title
lib_setting_arrays rst_header_chars
rst_header_chars=( "^" ":" "=" "~" "-" "_" "." )
lib_setting_vars rst_col_indent rst_col_limit rst_col_inc
rst_col_indent=0
rst_col_limit=72
rst_col_inc=2
lib_setting_vars rst_flow_hanging
rst_flow_hanging=false
lib_setting_vars rst_enum_start rst_enum_index
rst_enum_start=1
rst_enum_index=0
######
# Block Directive Settings
# $rst_code_line_start - If not `null`, this setting both (a)
# specifies the starting number to use when prefixing the lines
# of a code block and (b) enables line numbering in a code block.
# Default: `null`
lib_setting_vars --null rst_code_line_start
# $rst_admonitions[] - List of supported admonitions
# Default: ``attention caution danger error hint important
# note tip warning admonition``
lib_setting_arrays -ro rst_admonitions
rst_admonitions=(
attention caution danger error hint
important note tip warning admonition
)
# $rst_image_element - Name of the image directive to generate
# Default: ``image``
lib_setting_vars rst_image_element
rst_image_element=image
# $rst_image_strict - If set to ``true``, invalid image options
# generate an error; otherwise, they are ignored.
# Default: ``image``
lib_setting_vars rst_image_strict
rst_image_strict=true
######
# HTML Settings
# $rst_html_stylesheets[] - List of HTML stylesheets
lib_setting_arrays rst_html_stylesheets
}
doc_rst_lib_init() {
local level
for level in $(seq 1 7); do
eval "rst_h$level() { rst_header_n $level \"\$*\"; }"
done
}
######
# Text reflowing
# rst_indent - Increments indent and calls another generation function
rst_indent() {
local rst_col_indent=$((rst_col_indent + $rst_col_inc))
func_trace "indent=$rst_col_indent limit=$rst_col_limit"
run "$@"
}
rst_indent_str() { printf "%${rst_col_indent}s" ''; }
# rst_reflow - Rewraps text with current indent level and column limit
rst_reflow() {
local -a text
local indent=$(rst_indent_str)
local line="$indent"
while [ ${#*} -gt 0 ]; do
if $rst_flow_hanging && [ ${#text[*]} -gt 0 ]; then
local rst_flow_hanging=false
rst_indent rst_reflow "$@"
return
fi
local part=$1
shift
part=${part##[\t\n ]}
part=${part%%[\t\n ]}
if [ -z "$part" ]; then
text+=( "$line" )
[ -z "${line## }" ] || echo
line="$indent"
continue
fi
line="$line${line:+ }$part"
local next=''
local i=$rst_col_limit
while ! [ $rst_col_limit -lt ${#line} ]; do
if [ "${line[$i]}" != ' ' ]; then
i=$((i - 1))
continue
fi
# save last word for later
next="${line:$((i + 1))}${next:+ }$next"
part=${line:0:$i}
break
done
# do not emit until we spill
[ "$next" ] || continue
text+=( "$line" )
line=$next
done
[ "${line## }" ] && text+=( "$line" )
echo "${text[*]}"
}
rst_reflow() { echo "$*"; }
rst_trim() {
local var="$*"
var="${var#"${var%%[![:space:]]*}"}"
var="${var%"${var##*[![:space:]]}"}"
echo -n "$var"
}
######
# Document
rst_title() {
has_args 2 "$@"
rst_header '$' "$1"
rst_header '%' "$2"
}
######
# Headers
rst_header_n() {
has_args 2 "$@"
local level=$1
local str=$2
local char=${rst_header_chars[$((level - 1))]}
rst_header "$char" "$str"
}
rst_header() {
has_args 2 "$@"
local char=$1
local str=$2
echo "$str"
local bar=$(printf "%${#str}s" '')
echo "${bar// /$char}"
echo
}
######
# Blocks
rst_p() { rst_reflow "$@"; echo; }
rst_block_literal() {
rst_reflow "$1::"
shift
rst_indent rst_reflow "$@"
}
rst_block_quote() {
rst_indent rst_reflow "$@"
}
######
# Inline
rst_italic() { echo ":emphasis:\`$*\`"; }
rst_bold() { echo ":strong:\`$*\`"; }
rst_subscript() { echo ":subscript:\`$*\`"; }
rst_superscript() { echo ":superscript:\`$*\`"; }
rst_title_ref() { echo ":title:\`$*\`"; }
rst_literal() { echo ":literal:\`$*\`"; }
rst_code() { echo ":code:\`$*\`"; }
rst_math() { echo ":math:\`$*\`"; }
rst_pep() { echo ":PEP:\`$*\`"; }
rst_rfc() { echo ":RFC:\`$*\`"; }
######
# Lists
rst_bullet_item() {
local rst_flow_hanging=true
rst_reflow " - $1"
}
rst_bullet_items() {
local rst_indent=$((rst_indent + 2))
for_each rst_bullet_item "$@"
}
rst_enum_item() {
local rst_flow_hanging=true
rst_reflow "$rst_enum_index. $@"
$((rst_enum_index++))
}
rst_enum_items() {
local rst_enum_index=$rst_enum_start
for_each rst_enum_item "$@"
}
rst_def_item() {
echo "$1"
rst_reflow "$2"
}
rst_def_items() { for_each_pair rst_def_item "$@"; }
rst_option_item() {
echo "$1"
rst_indent rst_reflow "$2"
}
rst_option_items() { for_each_pair rst_option_item "$@"; }
######
# Directives
# rst_directive() - Generates a directive of the given type ($1) and
# using the given data ($2).
#
# $1 - The type of directive to emit (e.g. ``image``).
# $2 - Directive data (optional). The format depends on the type of
# directive ($1).
# $@ - Directive body
rst_directive() {
min_args 2 "$@"
local kind=$1
local data=$2
shift 2
echo ".. $kind::${data:+ }$data"
for_each_pair rst_directive_opt_print "${rst_directive_opts[@]}"
[ "$*" ] || return
[ -z "${rst_directive_opts[*]}" ] || echo
for_each rst_directive_line "$@"
echo
}
# rst_directive_line() - Prints a single line ($1) of a directive body,
# indented to align with the directive name.
rst_directive_line() { [ "$1" ] && echo " $1" || echo; }
######
# Directive Options
# rst_is_directive_opt() - Checks whether a string ($1) is a directive option.
# Returns: Success if the string is a valid directive option
rst_is_directive_opt() { [ "${1#:}" != "$1" -a "${1%:}" != "$1" ]; }
# rst_directive_opt_add() - Adds a directive option ($1) and value ($@)
# pair to ``$rst_directive_opts[]``.
# $1 - Option name
# $@ - Option values (optional)
rst_directive_opt_add() {
min_args 1 "$@"
local opt=$1
shift
rst_directive_opts+=( ":$opt:" "$*" )
}
# rst_directive_opt_print() - Prints a directive option ($1) and value ($2).
rst_directive_opt_print() { echo " $1 $2"; }
######
# Topic, Sidebar, Code
# rst_topic() - Generates a topic directive
# $1 - Topic Title
# $@ - Topic Body
rst_topic() {
min_args 1 "$@"
rst_directive 'topic' "$@"
}
# rst_sidebar() - Generates a `sidebar`.
# $1 - Title
# $2 - Subtitle (may be a `null` string)
# $@ - Sidebar body
rst_sidebar() {
min_args 2 "$@"
local title=$1
local subtitle=$2
shift 2
local -a opts=()
[ -z "$subtitle" ] || opts+=( ':subtitle:' "$subtitle" )
local -a rst_directive_opts=( "${opts[@]}" )
rst_directive 'sidebar' "$title" "$@"
}
# rst_block_code() - Generates a `code` block.
# $1 - Code language (see ``dev/tool/pygmentize``)
# $@ - Code block body
rst_block_code() {
min_args 2 "$@"
local language=$1
[ -z "$rst_block_code_line_start" ] \
|| opts+=( ":number-lines:" "$rst_block_code_line_start" )
local rst_directive_opts=( "${opts[@]}" )
rst_directive 'code' "$language" "$@"
}
######
# Admonisions
#
# For a list of supported admonisions, see ``$rst_admonitions[]``.
# rst_is_admonition() - Checks whether a string ($1) is a valid
# keyword.
rst_is_admonition() {
in_list "$kind" "${rst_admonitions[@]}" \
|| error "$1: invalid admonition"
}
# rst_admonition() - Produces the given type ($1) of admonition directive.
# $1 - Type of Admonition. See ``$rst_admonitions[]``.
rst_admonition() {
local kind=$1
rst_is_admonition "$kind"
rst_directive "$kind" "" "$@"
}
######
# Images and Figures
# rst_image() - Generates an image directive for the given url ($1) and
# image options ($@). The following options are supported:
#
# - ``:alt:``: Alternative image text.
# - ``:align:``: Conrols the alignment of the image.
# - ``:height:``, ``width``: Control the dimensions of the image.
# - ``:scale:``: Control the scale of the image (0-100).
# - ``:target:``: Makes the image into a hyperlink reference.
#
# The type of directive generated is controlled by the value of
# ``$rst_image_element``. The default value of ``image`` generates
# canonical image directives.
#
# If ``$rst_image_strict`` is ``true``, an invalid option will produce
# an ``error``. Otherwise, option processing will end. The invalid
# option, along with any other options, will be emitted as part of the
# directive body.
#
# Note: The value of ``$rst_image_strict`` does not impact validation
# of an option's values. If an invalid value is given for the ``scale``
# or ``align`` options, an ``error`` will always be produced.
#
# The ``rst_figure()`` function overrides these two settings and
# calls this function.
#
# $1 - The URL location of the image
# $@ - Image options (optional)
rst_image() {
local url=$1
shift
local -a opts=()
while [ "$*" ]; do
local opt=$1
rst_is_directive_opt "$opt" || break
opt=${opt#:}
opt=${opt%:}
local val=$2
case "$opt" in
(alt|height|width|target)
opts+=( ":$opt:" "$val" )
shift
;;
(scale)
[ "$val" -ge 0 -a "$val" -le 100 ] \
|| rst_image_error "$opt" "$val"
opts+=( ":$opt:" "$val %" )
shift
;;
(align)
rst_is_image_align "$val" \
|| rst_image_error "$opt" "$val"
opts+=( ":$opt:" "$val" )
shift
;;
(*)
! $rst_image_strict || rst_image_error '' "$opt"
break
;;
esac
shift
done
local -a rst_directive_opts=( "${opts[@]}" )
rst_directive "$rst_image_element" "$url" "$@"
}
# rst_is_image_align() - Checks whether a string ($1) is a valid
# image alignment option value.
# $1 - Align option value
# Returns: Success if the value is valid.
rst_is_image_align() {
local val=$1
case "$val" in
(top|middle|bottom|left|center|right) true ;;
(*) false ;;
esac
}
# rst_image_error() - Reports an error found when parsing an image
# option ($1) or option value ($2).
# $1 - The option that generated the error (optional)
# $2 - The value of the option
# $3 - The type of option error (optional)
rst_image_error() {
error "$2: unknown image${1+: }$1${1+: }option${3:+ }$3"
}
# rst_figure() - Generates a `figure`. See the ``rst_image()`` function
# for usage information.
rst_figure() {
local rst_image_element='figure'
local rst_image_strict=false
rst_image "$@"
}
######
# TODO: Tables
######
# Rendering
rst_to_html() {
local -a opts
if [ "${rst_html_css[*]}" ]; then
local IFS=','
opts+=( "--stylesheet=${rst_html_css[*]}" )
fi
run rst2html "${opts[@]}" "$@"
}
rst_to_latex() { run rst2latex "$@"; }
rst_to_pdf() {
rst_to_latex "$1" >"$tmp";
latex_to_pdf "$tmp" "$2"
}
Generated on Sat Jul 8 19:41:25 PDT 2017 by mcsh d14 v0.22.0.