Skip to content

Commit

Permalink
Support Whitespace tags for Objects
Browse files Browse the repository at this point in the history
This patch is the first step towards supporting the Whitespace Control tags.
It only implements support for the whitespace object tags `{{-` and `-}}`.

Support for the tags is more complicated since the `Tag.eval/3` function needs to support the `trim_previous` and `trim_next` arguments.
In some cases there are multiple of them and we need to forward them to further calls of `Solid.render/3`.

I'm very new to NimbleParsec and generally parsers so I'm posting this to get verification on the current path I have taken.
Some guidance would be appreciated if possible.

Needed by edgurgel#10
  • Loading branch information
Joel Ambass committed Feb 19, 2021
1 parent 2313212 commit 66a71da
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 31 deletions.
55 changes: 50 additions & 5 deletions lib/solid.ex
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ defmodule Solid do
Enum.reduce(text, {[], context}, fn entry, {acc, context} ->
try do
{result, context} = do_render(entry, context, options)
{result, acc, context} = maybe_trim(entry, result, acc, context)
{[result | acc], context}
catch
{:break_exp, partial_result, context} ->
Expand All @@ -87,6 +88,55 @@ defmodule Solid do
{Enum.reverse(result), context}
end

defp maybe_trim({:text, _data}, result, acc, context) do
result = maybe_trim_current(result, context)
context = Map.put(context, :trim_next, false)
{result, acc, context}
end

defp maybe_trim({:object, data}, result, acc, context) do
[trim_previous] = Keyword.get(data, :trim_previous)
acc = maybe_trim_previous(acc, trim_previous)

result = maybe_trim_current(result, context)

[trim_next] = Keyword.get(data, :trim_next)
context = Map.put(context, :trim_next, trim_next)

{result, acc, context}
end

defp maybe_trim({:tag, _data}, result, acc, context) do
context = Map.put(context, :trim_next, false)
{result, acc, context}
end

defp maybe_trim_current(result, context) do
if context.trim_next do
trim_leading(result)
else
result
end
end

defp maybe_trim_previous(acc, flag) do
if flag && !Enum.empty?(acc) do
[prev | tail] = acc
trimmed_prev = trim_trailing(prev)
[trimmed_prev | tail]
else
acc
end
end

defp trim_trailing([value]) do
[String.trim_trailing(value)]
end

defp trim_leading([value]) do
[String.trim_leading(value)]
end

defp do_render({:text, string}, context, _options), do: {string, context}

defp do_render({:object, object}, context, options) do
Expand All @@ -95,11 +145,6 @@ defmodule Solid do
end

defp do_render({:tag, tag}, context, options) do
{tag_text, context} = render_tag(tag, context, options)
{tag_text, context}
end

defp render_tag(tag, context, options) do
{result, context} = Tag.eval(tag, context, options)

if result do
Expand Down
5 changes: 3 additions & 2 deletions lib/solid/context.ex
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
defmodule Solid.Context do
defstruct vars: %{}, counter_vars: %{}, iteration_vars: %{}, cycle_state: %{}
defstruct vars: %{}, counter_vars: %{}, iteration_vars: %{}, cycle_state: %{}, trim_next: false

@type t :: %__MODULE__{
vars: Map.t(),
counter_vars: Map.t(),
iteration_vars: %{optional(String.t()) => term},
cycle_state: Map.t()
cycle_state: Map.t(),
trim_next: boolean
}
@type scope :: :counter_vars | :vars | :iteration_vars

Expand Down
55 changes: 31 additions & 24 deletions lib/solid/parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -96,18 +96,25 @@ defmodule Solid.Parser.Base do

argument = choice([value, field])

opening_object = string("{{")
closing_object = string("}}")
opening_tag = ignore(string("{%"))
closing_tag = ignore(string("%}"))

opening_tag = string("{%")
closing_tag = string("%}")
opening_objects = choice([
string("{{-") |> replace(true) |> tag(:trim_previous),
string("{{") |> replace(false) |> tag(:trim_previous)
])

closing_objects = choice([
string("-}}") |> replace(true) |> tag(:trim_next),
string("}}") |> replace(false) |> tag(:trim_next)
])

space =
string(" ")
|> times(min: 0)

text =
lookahead_not(choice([opening_object, opening_tag]))
lookahead_not(choice([opening_objects, opening_tag]))
|> utf8_string([], 1)
|> times(min: 1)
|> reduce({Enum, :join, []})
Expand Down Expand Up @@ -136,21 +143,21 @@ defmodule Solid.Parser.Base do
|> tag(:filter)

object =
ignore(opening_object)
opening_objects
|> ignore(space)
|> lookahead_not(closing_object)
|> lookahead_not(closing_objects)
|> tag(argument, :argument)
|> optional(tag(repeat(filter), :filters))
|> ignore(space)
|> ignore(closing_object)
|> concat(closing_objects)
|> tag(:object)

comment = string("comment")

end_comment = string("endcomment")

comment_tag =
ignore(opening_tag)
opening_tag
|> ignore(space)
|> ignore(comment)
|> ignore(space)
Expand All @@ -171,7 +178,7 @@ defmodule Solid.Parser.Base do
|> replace({-1, -1})

counter_tag =
ignore(opening_tag)
opening_tag
|> ignore(space)
|> concat(choice([increment, decrement]))
|> ignore(space)
Expand All @@ -181,7 +188,7 @@ defmodule Solid.Parser.Base do
|> tag(:counter_exp)

case_tag =
ignore(opening_tag)
opening_tag
|> ignore(space)
|> ignore(string("case"))
|> ignore(space)
Expand All @@ -190,7 +197,7 @@ defmodule Solid.Parser.Base do
|> ignore(closing_tag)

when_tag =
ignore(opening_tag)
opening_tag
|> ignore(space)
|> ignore(string("when"))
|> ignore(space)
Expand All @@ -201,7 +208,7 @@ defmodule Solid.Parser.Base do
|> tag(:when)

else_tag =
ignore(opening_tag)
opening_tag
|> ignore(space)
|> ignore(string("else"))
|> ignore(space)
Expand Down Expand Up @@ -258,15 +265,15 @@ defmodule Solid.Parser.Base do
|> repeat(choice([bool_and, bool_or]) |> concat(expression))

if_tag =
ignore(opening_tag)
opening_tag
|> ignore(space)
|> ignore(string("if"))
|> tag(boolean_expression, :expression)
|> ignore(closing_tag)
|> tag(parsec(:liquid_entry), :result)

elsif_tag =
ignore(opening_tag)
opening_tag
|> ignore(space)
|> ignore(string("elsif"))
|> tag(boolean_expression, :expression)
Expand All @@ -275,7 +282,7 @@ defmodule Solid.Parser.Base do
|> tag(:elsif_exp)

unless_tag =
ignore(opening_tag)
opening_tag
|> ignore(space)
|> ignore(string("unless"))
|> tag(boolean_expression, :expression)
Expand Down Expand Up @@ -304,7 +311,7 @@ defmodule Solid.Parser.Base do
|> ignore(closing_tag)

assign_tag =
ignore(opening_tag)
opening_tag
|> ignore(space)
|> ignore(string("assign"))
|> ignore(space)
Expand Down Expand Up @@ -352,7 +359,7 @@ defmodule Solid.Parser.Base do
|> reduce({Enum, :into, [%{}]})

for_tag =
ignore(opening_tag)
opening_tag
|> ignore(space)
|> ignore(string("for"))
|> ignore(space)
Expand All @@ -375,7 +382,7 @@ defmodule Solid.Parser.Base do
|> tag(:for_exp)

capture_tag =
ignore(opening_tag)
opening_tag
|> ignore(space)
|> ignore(string("capture"))
|> ignore(space)
Expand All @@ -392,15 +399,15 @@ defmodule Solid.Parser.Base do
|> tag(:capture_exp)

break_tag =
ignore(opening_tag)
opening_tag
|> ignore(space)
|> ignore(string("break"))
|> ignore(space)
|> ignore(closing_tag)
|> tag(:break_exp)

continue_tag =
ignore(opening_tag)
opening_tag
|> ignore(space)
|> ignore(string("continue"))
|> ignore(space)
Expand All @@ -415,7 +422,7 @@ defmodule Solid.Parser.Base do
|> ignore(closing_tag)

raw_tag =
ignore(opening_tag)
opening_tag
|> ignore(space)
|> ignore(string("raw"))
|> ignore(space)
Expand All @@ -425,7 +432,7 @@ defmodule Solid.Parser.Base do
|> tag(:raw_exp)

cycle_tag =
ignore(opening_tag)
opening_tag
|> ignore(space)
|> ignore(string("cycle"))
|> ignore(space)
Expand Down Expand Up @@ -475,7 +482,7 @@ defmodule Solid.Parser.Base do
all_tags =
if custom_tags != [] do
custom_tag =
ignore(opening_tag)
opening_tag
|> ignore(space)
|> concat(choice(custom_tags))
|> ignore(space)
Expand Down
1 change: 1 addition & 0 deletions test/cases/112/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
3 changes: 3 additions & 0 deletions test/cases/112/input.liquid
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{% assign my_variable = "tomato" %}
{% assign your_variable = "your" %}
! {{ your_variable }} ! {{- my_variable -}} !

0 comments on commit 66a71da

Please sign in to comment.