Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Unreleased

* Fix `.mot`/`.mopt` formatting collapsing the space in a single-element array
literal whose element is a Jinja expression (e.g. `{ {{ data[...] }} }`),
which produced an ambiguous `{{{ ... }}}` triple-brace sequence. A single
disambiguating space is now preserved whenever a restored Jinja expression's
own `{{`/`}}` delimiters would otherwise merge with an adjacent Modelica
array brace.
* Tweak default formatting for readability (issue #26):
* Keep a function call with a single argument on one line instead of breaking
the argument onto its own line, e.g. `pre(x)`, `der(y)`, `sin(z)`,
Expand Down
40 changes: 28 additions & 12 deletions internal/format/template.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,21 +324,37 @@ func reverseSub(text string, sub *subMap) (string, error) {
text = blockCommentedSubRegex.ReplaceAllString(text, "${1}")
text = trimWhitespaceBeforeRawPunctuation(text, sub)

var firstErr error
restored := normalSubRegex.ReplaceAllStringFunc(text, func(match string) string {
orig, err := sub.getText(match)
matches := normalSubRegex.FindAllStringIndex(text, -1)
var b strings.Builder
last := 0
for _, m := range matches {
start, end := m[0], m[1]
orig, err := sub.getText(text[start:end])
if err != nil {
if firstErr == nil {
firstErr = err
}
return match
return "", err
}
return orig
})
if firstErr != nil {
return "", firstErr

b.WriteString(text[last:start])

// A restored Jinja expression that starts/ends with its own `{{`/`}}`
// delimiters can collide with an immediately adjacent Modelica array
// brace (e.g. `{{{ ... }}}`), which is ambiguous when the template is
// re-rendered. Insert a single disambiguating space in that case. Only
// `{{`/`}}` expression delimiters are checked (not `{%`/`%}` control
// tags such as `{% raw %}`) so control-statement placeholders are
// unaffected.
if start > 0 && text[start-1] == '{' && strings.HasPrefix(orig, "{{") {
b.WriteByte(' ')
}
b.WriteString(orig)
if end < len(text) && text[end] == '}' && strings.HasSuffix(orig, "}}") {
b.WriteByte(' ')
}

last = end
}
return restored, nil
b.WriteString(text[last:])
return b.String(), nil
}

func trimWhitespaceBeforeRawPunctuation(text string, sub *subMap) string {
Expand Down
22 changes: 22 additions & 0 deletions internal/format/template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,28 @@ end LoopedArray;
a.Contains(formatted, "{% for building in data[\"building_load_files\"] %}")
}

// TestSingleElementArrayExpressionKeepsDisambiguatingSpace guards against the
// formatter collapsing `{ {{ expr }} }` into `{{{ expr }}}`. The Modelica
// array formatter normally strips the space inside a single-element array
// literal, but doing so here would make the restored Jinja expression's own
// `{{`/`}}` delimiters collide with the surrounding array braces, producing
// an ambiguous triple-brace sequence when the template is later rendered.
func TestSingleElementArrayExpressionKeepsDisambiguatingSpace(t *testing.T) {
a := require.New(t)
original := `model NominalArray
parameter Real a[3] = fill({ {{ data["nominal_values"]["boiler_efficiency"] }} });
end NominalArray;
`
var out bytes.Buffer
err := processTemplate(original, &out, Config{-1, false, false, false}, DialectJinja, "nominal-array.mot")
a.NoError(err)

formatted := out.String()
a.NotContains(formatted, `{{{`, "restored Jinja expression must not merge with a literal Modelica array brace")
a.NotContains(formatted, `}}}`, "restored Jinja expression must not merge with a literal Modelica array brace")
a.Contains(formatted, `{ {{ data["nominal_values"]["boiler_efficiency"] }} }`)
}

func TestInlineExpressionBeforeRawPunctuationDoesNotGainRenderedSpace(t *testing.T) {
a := require.New(t)
original := `model PumpTemplate
Expand Down