Fixing indentation of multiple partials

This fixes #16.
This commit is contained in:
Tyler Akins 2017-06-20 14:19:44 -05:00
parent e6787e71d5
commit 6376a9b817
No known key found for this signature in database
GPG Key ID: 8F3B8C432F4393BD
6 changed files with 101 additions and 18 deletions

40
API.md
View File

@ -9,15 +9,17 @@ mo()
Public: Template parser function. Writes templates to stdout.
* $0 - Name of the mo file, used for getting the help message.
* --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.
* $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.
@ -30,6 +32,12 @@ files
After we encounter two hyphens together, all the rest of the arguments are files.
MO_FAIL_ON_UNSET
----------------
shellcheck disable=SC2030
MO_FALSE_IS_EMPTY
-----------------
@ -160,6 +168,8 @@ 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)
@ -178,6 +188,8 @@ 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 () {
@ -263,11 +275,13 @@ moPartial()
Internal: Process a partial.
Indentation should be applied to the entire 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 "content" variable.
* $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
@ -344,6 +358,16 @@ Do not use variables without prefixes here if possible as this needs to check if
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".
moTestVarSet()
--------------
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.
moTrimChars()
-------------

45
mo
View File

@ -464,15 +464,20 @@ moIsStandalone() {
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
@ -518,7 +523,8 @@ moLoadFile() {
# The subshell removes any trailing newlines. We forcibly add
# a dot to the content to preserve all newlines.
# TODO: remove cat and replace with read loop?
# As a future optimization, it would be worth considering removing
# cat and replacing this with a read loop.
content=$(cat -- "$2"; echo '.')
len=$((${#content} - 1))
@ -561,7 +567,7 @@ moLoop() {
moParse() {
# Keep naming variables mo* here to not overwrite needed variables
# used in the string replacements
local moBlock moContent moCurrent moIsBeginning moTag
local moBlock moContent moCurrent moIsBeginning moNextIsBeginning moTag
moCurrent=$2
moIsBeginning=$3
@ -571,6 +577,7 @@ moParse() {
while [[ "${#moContent[@]}" -gt 1 ]]; do
moTrimWhitespace moTag "${moContent[1]}"
moNextIsBeginning=false
case $moTag in
'#'*)
@ -584,7 +591,7 @@ moParse() {
if moTest "$moTag"; then
# Show / loop / pass through function
if moIsFunction "$moTag"; then
#: TODO: Consider piping the output to moGetContent
#: Consider piping the output to moGetContent
#: so the lambda does not execute in a subshell?
moContent=$($moTag "${moBlock[0]}")
moParse "$moContent" "$moCurrent" false
@ -602,6 +609,8 @@ moParse() {
'>'*)
# Load partial - get name of file relative to cwd
moPartial moContent "${moContent[@]}" "$moIsBeginning" "$moCurrent"
moNextIsBeginning=${moContent[1]}
moContent=${moContent[0]}
;;
'/'*)
@ -639,7 +648,7 @@ moParse() {
'=')
# Change delimiters
# Any two non-whitespace sequences separated by whitespace.
# TODO
# This tag is ignored.
moStandaloneAllowed moContent "${moContent[@]}" "$moIsBeginning"
;;
@ -672,7 +681,7 @@ moParse() {
;;
esac
moIsBeginning=false
moIsBeginning=$moNextIsBeginning
moSplit moContent "$moContent" '{{' '}}'
done
@ -682,11 +691,18 @@ moParse() {
# Internal: Process a partial.
#
# Indentation should be applied to the entire 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 "content" variable.
# $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
@ -696,24 +712,27 @@ moParse() {
# Returns nothing.
moPartial() {
# Namespace variables here to prevent conflicts.
local moContent moFilename moIndent moPartial moStandalone moUnindented
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
(
# TODO: Remove dirname and use a function instead
# 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##*/}"
@ -727,7 +746,13 @@ moPartial() {
echo -n "$moPartial"
) || exit 1
local "$1" && moIndirect "$1" "$moContent"
# 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"
}

View File

View File

@ -0,0 +1,19 @@
With spacing
first line
second line
first line
second line
Without spacing
first line
second line
first line
second line
With text
first line
second line
text
first line
second line

View File

@ -0,0 +1,2 @@
first line
second line

View File

@ -0,0 +1,13 @@
With spacing
{{> indented-partials.partial}}
{{> indented-partials.partial}}
Without spacing
{{> indented-partials.partial}}
{{> indented-partials.partial}}
With text
{{> indented-partials.partial}}
text
{{> indented-partials.partial}}