Skip to content

Commit

Permalink
Simplify handling of attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
josevalim committed Dec 3, 2024
1 parent 63b45bf commit 6ecd75f
Showing 1 changed file with 64 additions and 80 deletions.
144 changes: 64 additions & 80 deletions lib/phoenix_live_view/tokenizer.ex
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ defmodule Phoenix.LiveView.Tokenizer do
%{
file: file,
column_offset: indentation + 1,
braces: 0,
braces: true,
context: [],
source: source,
indentation: indentation,
Expand Down Expand Up @@ -155,12 +155,11 @@ defmodule Phoenix.LiveView.Tokenizer do
handle_tag_open(rest, line, column + 1, text_to_acc, %{state | context: []})
end

defp handle_text("{" <> rest, line, column, buffer, acc, state) do
%{braces: 0} = state
defp handle_text("{" <> rest, line, column, buffer, acc, %{braces: true} = state) do
text_to_acc = text_to_acc(buffer, acc, line, column, state.context)

case handle_interpolation(rest, line, column + 1, [], state) do
{:ok, value, new_line, new_column, rest, state} ->
case handle_interpolation(rest, line, column + 1, [], 0, state) do
{:ok, value, new_line, new_column, rest} ->
acc = [{:body_expr, value, %{line: line, column: column}} | text_to_acc]
handle_text(rest, new_line, new_column, [], acc, state)

Expand Down Expand Up @@ -450,8 +449,10 @@ defmodule Phoenix.LiveView.Tokenizer do
defp handle_attribute(text, line, column, acc, state) do
case handle_attr_name(text, column, []) do
{:ok, name, new_column, rest} ->
acc = put_attr(acc, name, %{line: line, column: column})
handle_maybe_attr_value(rest, line, new_column, acc, state)
attr_meta = %{line: line, column: column}
{text, line, column, value} = handle_maybe_attr_value(rest, line, new_column, state)
acc = put_attr(acc, name, attr_meta, value)
handle_maybe_tag_open_end(text, line, column, acc, state)

{:error, message, column} ->
meta = %{line: line, column: column}
Expand All @@ -462,8 +463,8 @@ defmodule Phoenix.LiveView.Tokenizer do
## handle_root_attribute

defp handle_root_attribute(text, line, column, acc, state) do
case handle_interpolation(text, line, column, [], state) do
{:ok, value, new_line, new_column, rest, state} ->
case handle_interpolation(text, line, column, [], 0, state) do
{:ok, value, new_line, new_column, rest} ->
meta = %{line: line, column: column}
acc = put_attr(acc, :root, meta, {:expr, value, meta})
handle_maybe_tag_open_end(rest, new_line, new_column, acc, state)
Expand Down Expand Up @@ -502,55 +503,55 @@ defmodule Phoenix.LiveView.Tokenizer do

## handle_maybe_attr_value

defp handle_maybe_attr_value("\r\n" <> rest, line, _column, acc, state) do
handle_maybe_attr_value(rest, line + 1, state.column_offset, acc, state)
defp handle_maybe_attr_value("\r\n" <> rest, line, _column, state) do
handle_maybe_attr_value(rest, line + 1, state.column_offset, state)
end

defp handle_maybe_attr_value("\n" <> rest, line, _column, acc, state) do
handle_maybe_attr_value(rest, line + 1, state.column_offset, acc, state)
defp handle_maybe_attr_value("\n" <> rest, line, _column, state) do
handle_maybe_attr_value(rest, line + 1, state.column_offset, state)
end

defp handle_maybe_attr_value(<<c::utf8, rest::binary>>, line, column, acc, state)
defp handle_maybe_attr_value(<<c::utf8, rest::binary>>, line, column, state)
when c in @space_chars do
handle_maybe_attr_value(rest, line, column + 1, acc, state)
handle_maybe_attr_value(rest, line, column + 1, state)
end

defp handle_maybe_attr_value("=" <> rest, line, column, acc, state) do
handle_attr_value_begin(rest, line, column + 1, acc, state)
defp handle_maybe_attr_value("=" <> rest, line, column, state) do
handle_attr_value_begin(rest, line, column + 1, state)
end

defp handle_maybe_attr_value(text, line, column, acc, state) do
handle_maybe_tag_open_end(text, line, column, acc, state)
defp handle_maybe_attr_value(text, line, column, _state) do
{text, line, column, nil}
end

## handle_attr_value_begin

defp handle_attr_value_begin("\r\n" <> rest, line, _column, acc, state) do
handle_attr_value_begin(rest, line + 1, state.column_offset, acc, state)
defp handle_attr_value_begin("\r\n" <> rest, line, _column, state) do
handle_attr_value_begin(rest, line + 1, state.column_offset, state)
end

defp handle_attr_value_begin("\n" <> rest, line, _column, acc, state) do
handle_attr_value_begin(rest, line + 1, state.column_offset, acc, state)
defp handle_attr_value_begin("\n" <> rest, line, _column, state) do
handle_attr_value_begin(rest, line + 1, state.column_offset, state)
end

defp handle_attr_value_begin(<<c::utf8, rest::binary>>, line, column, acc, state)
defp handle_attr_value_begin(<<c::utf8, rest::binary>>, line, column, state)
when c in @space_chars do
handle_attr_value_begin(rest, line, column + 1, acc, state)
handle_attr_value_begin(rest, line, column + 1, state)
end

defp handle_attr_value_begin("\"" <> rest, line, column, acc, state) do
handle_attr_value_quote(rest, ?", line, column + 1, [], acc, state)
defp handle_attr_value_begin("\"" <> rest, line, column, state) do
handle_attr_value_quote(rest, ?", line, column + 1, [], state)
end

defp handle_attr_value_begin("'" <> rest, line, column, acc, state) do
handle_attr_value_quote(rest, ?', line, column + 1, [], acc, state)
defp handle_attr_value_begin("'" <> rest, line, column, state) do
handle_attr_value_quote(rest, ?', line, column + 1, [], state)
end

defp handle_attr_value_begin("{" <> rest, line, column, acc, state) do
handle_attr_value_as_expr(rest, line, column + 1, acc, state)
defp handle_attr_value_begin("{" <> rest, line, column, state) do
handle_attr_value_as_expr(rest, line, column + 1, state)
end

defp handle_attr_value_begin(_text, line, column, _acc, state) do
defp handle_attr_value_begin(_text, line, column, state) do
message =
"invalid attribute value after `=`. Expected either a value between quotes " <>
"(such as \"value\" or \'value\') or an Elixir expression between curly brackets (such as `{expr}`)"
Expand All @@ -561,27 +562,26 @@ defmodule Phoenix.LiveView.Tokenizer do

## handle_attr_value_quote

defp handle_attr_value_quote("\r\n" <> rest, delim, line, _column, buffer, acc, state) do
defp handle_attr_value_quote("\r\n" <> rest, delim, line, _column, buffer, state) do
column = state.column_offset
handle_attr_value_quote(rest, delim, line + 1, column, ["\r\n" | buffer], acc, state)
handle_attr_value_quote(rest, delim, line + 1, column, ["\r\n" | buffer], state)
end

defp handle_attr_value_quote("\n" <> rest, delim, line, _column, buffer, acc, state) do
defp handle_attr_value_quote("\n" <> rest, delim, line, _column, buffer, state) do
column = state.column_offset
handle_attr_value_quote(rest, delim, line + 1, column, ["\n" | buffer], acc, state)
handle_attr_value_quote(rest, delim, line + 1, column, ["\n" | buffer], state)
end

defp handle_attr_value_quote(<<delim, rest::binary>>, delim, line, column, buffer, acc, state) do
defp handle_attr_value_quote(<<delim, rest::binary>>, delim, line, column, buffer, _state) do
value = buffer_to_string(buffer)
acc = put_attr_value(acc, {:string, value, %{delimiter: delim}})
handle_maybe_tag_open_end(rest, line, column + 1, acc, state)
{rest, line, column + 1, {:string, value, %{delimiter: delim}}}
end

defp handle_attr_value_quote(<<c::utf8, rest::binary>>, delim, line, column, buffer, acc, state) do
handle_attr_value_quote(rest, delim, line, column + 1, [char_or_bin(c) | buffer], acc, state)
defp handle_attr_value_quote(<<c::utf8, rest::binary>>, delim, line, column, buffer, state) do
handle_attr_value_quote(rest, delim, line, column + 1, [char_or_bin(c) | buffer], state)
end

defp handle_attr_value_quote(<<>>, delim, line, column, _buffer, _acc, state) do
defp handle_attr_value_quote(<<>>, delim, line, column, _buffer, state) do
message = """
expected closing `#{<<delim>>}` for attribute value
Expand All @@ -606,11 +606,10 @@ defmodule Phoenix.LiveView.Tokenizer do

## handle_attr_value_as_expr

defp handle_attr_value_as_expr(text, line, column, acc, %{braces: 0} = state) do
case handle_interpolation(text, line, column, [], state) do
{:ok, value, new_line, new_column, rest, state} ->
acc = put_attr_value(acc, {:expr, value, %{line: line, column: column}})
handle_maybe_tag_open_end(rest, new_line, new_column, acc, state)
defp handle_attr_value_as_expr(text, line, column, state) do
case handle_interpolation(text, line, column, [], 0, state) do
{:ok, value, new_line, new_column, rest} ->
{rest, new_line, new_column, {:expr, value, %{line: line, column: column}}}

{:error, message} ->
# We do column - 1 to point to the opening {
Expand All @@ -621,42 +620,40 @@ defmodule Phoenix.LiveView.Tokenizer do

## handle_interpolation

defp handle_interpolation("\r\n" <> rest, line, _column, buffer, state) do
handle_interpolation(rest, line + 1, state.column_offset, ["\r\n" | buffer], state)
defp handle_interpolation("\r\n" <> rest, line, _column, buffer, braces, state) do
handle_interpolation(rest, line + 1, state.column_offset, ["\r\n" | buffer], braces, state)
end

defp handle_interpolation("\n" <> rest, line, _column, buffer, state) do
handle_interpolation(rest, line + 1, state.column_offset, ["\n" | buffer], state)
defp handle_interpolation("\n" <> rest, line, _column, buffer, braces, state) do
handle_interpolation(rest, line + 1, state.column_offset, ["\n" | buffer], braces, state)
end

defp handle_interpolation("}" <> rest, line, column, buffer, %{braces: 0} = state) do
defp handle_interpolation("}" <> rest, line, column, buffer, 0, _state) do
value = buffer_to_string(buffer)
{:ok, value, line, column + 1, rest, state}
{:ok, value, line, column + 1, rest}
end

defp handle_interpolation(~S(\}) <> rest, line, column, buffer, state) do
handle_interpolation(rest, line, column + 2, [~S(\}) | buffer], state)
defp handle_interpolation(~S(\}) <> rest, line, column, buffer, braces, state) do
handle_interpolation(rest, line, column + 2, [~S(\}) | buffer], braces, state)
end

defp handle_interpolation(~S(\{) <> rest, line, column, buffer, state) do
handle_interpolation(rest, line, column + 2, [~S(\{) | buffer], state)
defp handle_interpolation(~S(\{) <> rest, line, column, buffer, braces, state) do
handle_interpolation(rest, line, column + 2, [~S(\{) | buffer], braces, state)
end

defp handle_interpolation("}" <> rest, line, column, buffer, state) do
state = pop_brace(state)
handle_interpolation(rest, line, column + 1, ["}" | buffer], state)
defp handle_interpolation("}" <> rest, line, column, buffer, braces, state) do
handle_interpolation(rest, line, column + 1, ["}" | buffer], braces - 1, state)
end

defp handle_interpolation("{" <> rest, line, column, buffer, state) do
state = push_brace(state)
handle_interpolation(rest, line, column + 1, ["{" | buffer], state)
defp handle_interpolation("{" <> rest, line, column, buffer, braces, state) do
handle_interpolation(rest, line, column + 1, ["{" | buffer], braces + 1, state)
end

defp handle_interpolation(<<c::utf8, rest::binary>>, line, column, buffer, state) do
handle_interpolation(rest, line, column + 1, [char_or_bin(c) | buffer], state)
defp handle_interpolation(<<c::utf8, rest::binary>>, line, column, buffer, braces, state) do
handle_interpolation(rest, line, column + 1, [char_or_bin(c) | buffer], braces, state)
end

defp handle_interpolation(<<>>, _line, _column, _buffer, _state) do
defp handle_interpolation(<<>>, _line, _column, _buffer, _braces, _state) do
{:error,
"""
expected closing `}` for expression
Expand Down Expand Up @@ -699,12 +696,7 @@ defmodule Phoenix.LiveView.Tokenizer do
defp trim_context([:comment_end, :comment_start | [_ | _] = rest]), do: trim_context(rest)
defp trim_context(rest), do: Enum.reverse(rest)

defp put_attr([{type, name, attrs, meta} | acc], attr, attr_meta, value \\ nil) do
attrs = [{attr, value, attr_meta} | attrs]
[{type, name, attrs, meta} | acc]
end

defp put_attr_value([{type, name, [{attr, _value, attr_meta} | attrs], meta} | acc], value) do
defp put_attr([{type, name, attrs, meta} | acc], attr, attr_meta, value) do
attrs = [{attr, value, attr_meta} | attrs]
[{type, name, attrs, meta} | acc]
end
Expand All @@ -723,14 +715,6 @@ defmodule Phoenix.LiveView.Tokenizer do
[{type, name, attrs, meta} | acc]
end

defp push_brace(state) do
%{state | braces: state.braces + 1}
end

defp pop_brace(state) do
%{state | braces: state.braces - 1}
end

defp strip_text_token_fully(tokens) do
with [{:text, text, _} | rest] <- tokens,
"" <- String.trim_leading(text) do
Expand Down

0 comments on commit 6ecd75f

Please sign in to comment.