Fix standalone tag detection

This commit is contained in:
Tyler Akins 2023-04-08 19:06:04 -05:00
parent 72a0e426c7
commit a237d23a2d
No known key found for this signature in database
GPG Key ID: 8F3B8C432F4393BD
2 changed files with 40 additions and 19 deletions

50
mo
View File

@ -181,7 +181,7 @@ mo() (
mo::debug "Debug enabled" mo::debug "Debug enabled"
mo::content moContent "${moFiles[@]}" || return 1 mo::content moContent "${moFiles[@]}" || return 1
mo::parse moResult "$moContent" "" "" "{{" "}}" "" mo::parse moResult "$moContent" "" "" "{{" "}}" "" $'\n'
echo -n "${moResult[0]}${moResult[1]}" echo -n "${moResult[0]}${moResult[1]}"
) )
@ -423,11 +423,20 @@ mo::chomp() {
# $5 - Open delimiter ("{{") # $5 - Open delimiter ("{{")
# $6 - Close delimiter ("}}") # $6 - Close delimiter ("}}")
# $7 - Fast mode (skip to end of block) if non-empty # $7 - Fast mode (skip to end of block) if non-empty
# $8 - Starting standalone content hack
# #
# The standalone content is a trick to make the standalone tag detection
# possible. When it's set to content with a newline and if the tag supports it,
# the standalone content check happens. This check ensures only whitespace is
# after the last newline up to the tag, and only whitespace is after the tag up
# to the next newline. If that is the case, remove whitespace and the trailing
# newline. By setting this to $'\n', we're saying we are at the beginning of
# content.
# #
# Array has the following elements # Array has the following elements
# [0] - Parsed content # [0] - Parsed content
# [1] - Unparsed content after the closing tag # [1] - Unparsed content after the closing tag
# [2] - Standalone content hack
# #
# Returns nothing. # Returns nothing.
mo::parse() { mo::parse() {
@ -438,15 +447,9 @@ mo::parse() {
moOpenDelimiter=$5 moOpenDelimiter=$5
moCloseDelimiter=$6 moCloseDelimiter=$6
moFastMode=$7 moFastMode=$7
moStandaloneContent=$8
moResult="" moResult=""
moRemainder="" moRemainder=""
# This is a trick to make the standalone tag detection believe it's on a
# new line because there's no other way to easily tell the difference
# between a lone tag on a line and two tags where the first one evaluated
# to an empty string. Whenever a standalone tag is encountered, this trick
# needs to be reset to a newline.
moStandaloneContent=$'\n'
mo::debug "Starting parse, current: $moCurrent, ending tag: $moCurrentBlock, fast: $moFastMode" mo::debug "Starting parse, current: $moCurrent, ending tag: $moCurrentBlock, fast: $moFastMode"
while [[ "${#moContent}" -gt 0 ]]; do while [[ "${#moContent}" -gt 0 ]]; do
@ -509,8 +512,6 @@ mo::parse() {
moResult=${moParseChunk[0]} moResult=${moParseChunk[0]}
moContent=${moParseChunk[1]} moContent=${moParseChunk[1]}
# See above for a comment detailing this standalone trick
moStandaloneContent=${moParseChunk[2]} moStandaloneContent=${moParseChunk[2]}
else else
moResult="$moResult$moContent" moResult="$moResult$moContent"
@ -518,7 +519,7 @@ mo::parse() {
fi fi
done done
local "$1" && mo::indirectArray "$1" "$moResult" "$moRemainder" local "$1" && mo::indirectArray "$1" "$moResult" "$moRemainder" "$moStandaloneContent"
} }
@ -564,7 +565,7 @@ mo::parseBlock() {
fi fi
# Get contents of block after parsing # Get contents of block after parsing
mo::parse moParseResult "$moContent" "$moCurrent" "${moArgs[1]}" "$moOpenDelimiter" "$moCloseDelimiter" "" mo::parse moParseResult "$moContent" "$moCurrent" "${moArgs[1]}" "$moOpenDelimiter" "$moCloseDelimiter" "" "$moStandaloneContent"
# Pass contents to function # Pass contents to function
mo::evaluateFunction moResult "${moParseResult[0]}" "${moArgs[@]:1}" mo::evaluateFunction moResult "${moParseResult[0]}" "${moArgs[@]:1}"
@ -574,6 +575,9 @@ mo::parseBlock() {
if mo::standaloneCheck "$moStandaloneContent" "$moContent"; then if mo::standaloneCheck "$moStandaloneContent" "$moContent"; then
mo::standaloneProcessBefore moPrevious "$moPrevious" mo::standaloneProcessBefore moPrevious "$moPrevious"
mo::standaloneProcessAfter moContent "$moContent" mo::standaloneProcessAfter moContent "$moContent"
moStandaloneContent=$'\n'
else
moStandaloneContent=""
fi fi
moArrayName=${moArgs[1]} moArrayName=${moArgs[1]}
@ -583,34 +587,38 @@ mo::parseBlock() {
# No elements # No elements
if [[ "$moInvertBlock" == "true" ]]; then if [[ "$moInvertBlock" == "true" ]]; then
# Show the block # Show the block
mo::parse moParseResult "$moContent" "$moArrayName" "$moArrayName" "$moOpenDelimiter" "$moCloseDelimiter" "" mo::parse moParseResult "$moContent" "$moArrayName" "$moArrayName" "$moOpenDelimiter" "$moCloseDelimiter" "" "$moStandaloneContent"
moResult=${moParseResult[0]} moResult=${moParseResult[0]}
else else
# Skip the block processing # Skip the block processing
mo::parse moParseResult "$moContent" "$moCurrent" "$moArrayName" "$moOpenDelimiter" "$moCloseDelimiter" "FAST-EMPTY" mo::parse moParseResult "$moContent" "$moCurrent" "$moArrayName" "$moOpenDelimiter" "$moCloseDelimiter" "FAST-EMPTY" "$moStandaloneContent"
moResult="" moResult=""
fi fi
else else
if [[ "$moInvertBlock" == "true" ]]; then if [[ "$moInvertBlock" == "true" ]]; then
# Skip the block processing # Skip the block processing
mo::parse moParseResult "$moContent" "$moCurrent" "$moArrayName" "$moOpenDelimiter" "$moCloseDelimiter" "FAST-EMPTY" mo::parse moParseResult "$moContent" "$moCurrent" "$moArrayName" "$moOpenDelimiter" "$moCloseDelimiter" "FAST-EMPTY" "$moStandaloneContent"
moResult="" moResult=""
else else
moResult="" moResult=""
# Process for each element in the array # Process for each element in the array
for moArrayIndex in "${moArrayIndexes[@]}"; do for moArrayIndex in "${moArrayIndexes[@]}"; do
mo::debug "Iterate over array using element: $moArrayName.$moArrayIndex" mo::debug "Iterate over array using element: $moArrayName.$moArrayIndex"
mo::parse moParseResult "$moContent" "$moArrayName.$moArrayIndex" "${moArgs[1]}" "$moOpenDelimiter" "$moCloseDelimiter" "" mo::parse moParseResult "$moContent" "$moArrayName.$moArrayIndex" "${moArgs[1]}" "$moOpenDelimiter" "$moCloseDelimiter" "" "$moStandaloneContent"
moResult="$moResult${moParseResult[0]}" moResult="$moResult${moParseResult[0]}"
done done
fi fi
fi fi
moContent=${moParseResult[1]} moContent=${moParseResult[1]}
moStandaloneContent=${moParseResult[2]}
else else
if mo::standaloneCheck "$moStandaloneContent" "$moContent"; then if mo::standaloneCheck "$moStandaloneContent" "$moContent"; then
mo::standaloneProcessBefore moPrevious "$moPrevious" mo::standaloneProcessBefore moPrevious "$moPrevious"
mo::standaloneProcessAfter moContent "$moContent" mo::standaloneProcessAfter moContent "$moContent"
moStandaloneContent=$'\n'
else
moStandaloneContent=""
fi fi
# Variable, value, or list of mixed things # Variable, value, or list of mixed things
@ -618,15 +626,16 @@ mo::parseBlock() {
if mo::isTruthy "$moResult" "$moInvertBlock"; then if mo::isTruthy "$moResult" "$moInvertBlock"; then
mo::debug "Block is truthy: $moResult" mo::debug "Block is truthy: $moResult"
mo::parse moParseResult "$moContent" "$moCurrent" "${moArgs[1]}" "$moOpenDelimiter" "$moCloseDelimiter" "" mo::parse moParseResult "$moContent" "$moCurrent" "${moArgs[1]}" "$moOpenDelimiter" "$moCloseDelimiter" "" "$moStandaloneContent"
else else
mo::debug "Block is falsy: $moResult" mo::debug "Block is falsy: $moResult"
mo::parse moParseResult "$moContent" "$moCurrent" "${moArgs[1]}" "$moOpenDelimiter" "$moCloseDelimiter" "FAST-FALSY" mo::parse moParseResult "$moContent" "$moCurrent" "${moArgs[1]}" "$moOpenDelimiter" "$moCloseDelimiter" "FAST-FALSY" "$moStandaloneContent"
moParseResult[0]="" moParseResult[0]=""
fi fi
moResult=${moParseResult[0]} moResult=${moParseResult[0]}
moContent=${moParseResult[1]} moContent=${moParseResult[1]}
moStandaloneContent=${moParseResult[2]}
fi fi
local "$1" && mo::indirectArray "$1" "$moPrevious$moResult" "$moContent" "$moStandaloneContent" local "$1" && mo::indirectArray "$1" "$moPrevious$moResult" "$moContent" "$moStandaloneContent"
@ -696,7 +705,7 @@ mo::parsePartial() {
fi fi
# Delimiters are reset when loading a new partial # Delimiters are reset when loading a new partial
mo::parse moResult "$moResult" "$moCurrent" "" "{{" "}}" "" mo::parse moResult "$moResult" "$moCurrent" "" "{{" "}}" "" $'\n'
# Fix bash handling of subshells and keep trailing whitespace. # Fix bash handling of subshells and keep trailing whitespace.
echo -n "${moResult[0]}${moResult[1]}." echo -n "${moResult[0]}${moResult[1]}."
@ -1500,6 +1509,7 @@ mo::standaloneCheck() {
if [[ "$moContent" != *"$moN"* ]]; then if [[ "$moContent" != *"$moN"* ]]; then
mo::debug "Not a standalone tag - no newline before" mo::debug "Not a standalone tag - no newline before"
return 1 return 1
fi fi
@ -1509,6 +1519,7 @@ mo::standaloneCheck() {
if [[ -n "$moContent" ]]; then if [[ -n "$moContent" ]]; then
mo::debug "Not a standalone tag - non-whitespace detected before tag" mo::debug "Not a standalone tag - non-whitespace detected before tag"
return 1 return 1
fi fi
@ -1520,6 +1531,7 @@ mo::standaloneCheck() {
if [[ -n "$moContent" ]]; then if [[ -n "$moContent" ]]; then
mo::debug "Not a standalone tag - non-whitespace detected after tag" mo::debug "Not a standalone tag - non-whitespace detected after tag"
return 1 return 1
fi fi

9
tests/internal-whitespace Executable file
View File

@ -0,0 +1,9 @@
#!/usr/bin/env bash
cd "${0%/*}" || exit 1
. ../run-tests
boolean=true
template=$' | {{#boolean}} {{! Important Whitespace }}\n {{/boolean}} | \n'
expected=$' | \n | \n'
runTest