Skip to content

Add option to warn when missing semicolons #1402

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
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
15 changes: 13 additions & 2 deletions lib/live_view_native/swiftui/rules_parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ defmodule LiveViewNative.SwiftUI.RulesParser do

alias LiveViewNative.SwiftUI.RulesParser.Modifiers
alias LiveViewNative.SwiftUI.RulesParser.Parser
require Logger

def parse(rules, opts \\ []) do
file = Keyword.get(opts, :file) || ""
module = Keyword.get(opts, :module) || ""
line = Keyword.get(opts, :line) || 1
variable_context = Keyword.get(opts, :variable_context, Elixir)
expect_semicolons? = Keyword.get(opts, :expect_semicolons?, false)

context =
opts
|> Keyword.get(:context, %{})
|> Map.put_new(:file, file)
|> Map.put_new(:source_line, line)
|> Map.put_new(:module, module)
|> Map.put_new(:expect_semicolons?, expect_semicolons?)
|> Map.put_new(
:annotations,
Application.get_env(:live_view_native_stylesheet, :annotations, false)
Expand All @@ -35,10 +38,12 @@ defmodule LiveViewNative.SwiftUI.RulesParser do
|> Parser.error_from_result()

case result do
{:ok, [output], _unconsumed = "", _context, _current_line_and_offset, _} ->
{:ok, [output], warnings} ->
log_warnings(warnings, file)
output

{:ok, output, _unconsumed = "", _context, _current_line_and_offset, _} ->
{:ok, output, warnings} ->
log_warnings(warnings, file)
output

{:error, message, _unconsumed, _context, {line, _}, _} ->
Expand All @@ -48,4 +53,10 @@ defmodule LiveViewNative.SwiftUI.RulesParser do
line: line
end
end

def log_warnings(warnings, file) do
for {message, {line, _}, _offset} <- Enum.reverse(warnings) do
IO.warn(message, line: line, file: file)
end
end
end
1 change: 1 addition & 0 deletions lib/live_view_native/swiftui/rules_parser/modifiers.ex
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ defmodule LiveViewNative.SwiftUI.RulesParser.Modifiers do
ignore_whitespace()
|> concat(modifier_name())
|> concat(modifier_brackets.(nested: false))
|> expect_semicolon_or_warn()
|> post_traverse({PostProcessors, :to_function_call_ast, []}),
export_combinator: true
)
Expand Down
26 changes: 17 additions & 9 deletions lib/live_view_native/swiftui/rules_parser/parser.ex
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ defmodule LiveViewNative.SwiftUI.RulesParser.Parser do
choices ++
if generate_error? do
[
error_parser
|> put_error(
put_error(
error_parser,
"Expected one of the following:\n" <>
label_from_named_choices(named_choices) <> "\n",
show_incorrect_text?: show_incorrect_text?
Expand All @@ -49,16 +49,19 @@ defmodule LiveViewNative.SwiftUI.RulesParser.Parser do
end

def expect(combinator \\ empty(), combinator_2, opts) do
error_parser = Keyword.get(opts, :error_parser, non_whitespace())
error_message = Keyword.get(opts, :error_message)
generate_error? = Keyword.get(opts, :generate_error?, true)
opts =
opts
|> Keyword.update(:error_parser, non_whitespace(), &(&1 || empty()))
|> Keyword.update(:error_message, "", & &1)
|> Keyword.update(:generate_error?, true, & &1)
|> Keyword.update(:warning, false, & &1)

combinator
|> concat(
if generate_error? do
if opts[:generate_error?] do
choice([
combinator_2,
put_error(error_parser, error_message, opts)
put_error(opts[:error_parser], opts[:error_message], opts)
])
else
combinator_2
Expand Down Expand Up @@ -150,8 +153,13 @@ defmodule LiveViewNative.SwiftUI.RulesParser.Parser do
{message, position, byte_offset} = Error.context_to_error_message(context)
{:error, message, rest, context, position, byte_offset}

result ->
result
{:ok, output, _unconsumed = "", %{context: %Context{} = context}, _, _} ->
warnings =
Enum.map(context.warnings, fn warning ->
Error.context_to_error_message(context, warning)
end)

{:ok, output, warnings}
end
end
end
15 changes: 8 additions & 7 deletions lib/live_view_native/swiftui/rules_parser/parser/context.ex
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ defmodule LiveViewNative.SwiftUI.RulesParser.Parser.Context do
source: "",
source_lines: [],
errors: [],
warnings: [],
highlight_error: true,
# Where in the code does the input start?
# Useful for localizing errors when parsing sigil text
Expand Down Expand Up @@ -45,14 +46,14 @@ defmodule LiveViewNative.SwiftUI.RulesParser.Parser.Context do
if is_frozen?(context) and not error.forced? do
context
else
path = [:context, Access.key(:errors)]
path =
if error.is_warning? do
[:context, Access.key(:warnings)]
else
[:context, Access.key(:errors)]
end

{_, context} =
get_and_update_in(context, path, fn
existing_errors -> {[error | existing_errors], [error | existing_errors]}
end)

context
update_in(context, path, &[error | &1])
end
end

Expand Down
106 changes: 57 additions & 49 deletions lib/live_view_native/swiftui/rules_parser/parser/error.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,70 +9,62 @@ defmodule LiveViewNative.SwiftUI.RulesParser.Parser.Error do
:line,
:byte_offset,
:error_message,
forced?: false
forced?: false,
is_warning?: false
])

def put_error(
rest,
args,
context,
_,
_,
error_message,
opts \\ []
)

def put_error(
rest,
[] = arg,
context,
{line, _offset},
byte_offset,
error_message,
opts
) do
# IO.inspect({[], rest, error_message}, label: "error[0]")

context =
Context.put_new_error(context, rest, %__MODULE__{
incorrect_text: "",
line: line,
byte_offset: byte_offset,
error_message: error_message,
show_incorrect_text?: Keyword.get(opts, :show_incorrect_text?, false),
forced?: Keyword.get(opts, :force_error?, false)
})

{rest, arg, context}
end

def put_error(
rest,
[matched_text | _] = args,
context,
{line, _offset},
byte_offset,
error_message,
opts
opts \\ []
) do
# IO.inspect({matched_text, rest, error_message}, label: "error[0]")
error = %__MODULE__{
incorrect_text: List.first(args, ""),
line: line,
byte_offset: byte_offset,
error_message: error_message,
show_incorrect_text?: Keyword.get(opts, :show_incorrect_text?, false),
forced?: Keyword.get(opts, :force_error?, false),
is_warning?: false
}

context =
Context.put_new_error(context, rest, %__MODULE__{
incorrect_text: matched_text,
line: line,
byte_offset: byte_offset,
error_message: error_message,
show_incorrect_text?: Keyword.get(opts, :show_incorrect_text?, false),
forced?: Keyword.get(opts, :force_error?, false)
})
case opts[:warning] do
true ->
# Always treat error as warning
Context.put_new_error(context, rest, %{error | is_warning?: true})

false ->
# Never treat error as warning
Context.put_new_error(context, rest, error)

nil ->
# Never treat error as warning
Context.put_new_error(context, rest, error)

optional_warning_key ->
# The error is an optional warning
# Only log the warning if value in `context[<key>]` is true
if get_in(context, [Access.key(optional_warning_key)]) == true do
Context.put_new_error(context, rest, %{error | is_warning?: true})
else
context
end
end

{rest, args, context}
end

def context_to_error_message(context) do
[%__MODULE__{} = error | _] = Enum.reverse(context.errors)
context_to_error_message(context, error)
end

def context_to_error_message(context, %__MODULE__{} = error) do
error_message = error.error_message
line = error.line
incorrect_text = error.incorrect_text
Expand Down Expand Up @@ -144,15 +136,31 @@ defmodule LiveViewNative.SwiftUI.RulesParser.Parser.Error do
""
end

header =
if error.is_warning? do
"#{error_message}#{maybe_but_got}"
else
"Unsupported input:"
end

footer =
if error.is_warning? do
""
else
"""

#{error_message}#{maybe_but_got}
"""
end

message = """
Unsupported input:
#{header}
#{line_spacer} |
#{lines}
#{line_spacer} |

#{error_message}#{maybe_but_got}
#{footer}
"""

{message, {source_line, 0}, error.byte_offset}
{String.trim(message), {source_line, 0}, error.byte_offset}
end
end
13 changes: 13 additions & 0 deletions lib/live_view_native/swiftui/rules_parser/tokens.ex
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,19 @@ defmodule LiveViewNative.SwiftUI.RulesParser.Tokens do
combinator |> ignore(optional(whitespace(min: 1)))
end

def expect_semicolon_or_warn(combinator) do
combinator
|> concat(
ignore(string(";"))
|> expect(
warning: :expect_semicolons?,
error_message: "Expected a ‘;’",
error_parser: empty(),
show_incorrect_text?: false
)
)
end

# @tuple_children [
# parsec(:nested_attribute),
# atom(),
Expand Down
4 changes: 2 additions & 2 deletions mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ defmodule LiveViewNative.SwiftUI.MixProject do
{:makeup_json, "~> 0.1.0", only: [:docs, :test]},
{:makeup_eex, ">= 0.1.1"},
{:floki, ">= 0.30.0", only: :test},
{:live_view_native, "~> 0.3.0-rc.3"},
{:live_view_native_stylesheet, "~> 0.3.0-rc.3", only: :test},
{:live_view_native, github: "liveview-native/live_view_native", branch: "main"},
{:live_view_native_stylesheet, github: "liveview-native/live_view_native_stylesheet", branch: "main", only: :test},
{:live_view_native_test, github: "liveview-native/live_view_native_test", branch: "main", only: :test},
{:nimble_parsec, "~> 1.3"}
]
Expand Down
4 changes: 2 additions & 2 deletions mix.lock
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
"ex_doc": {:hex, :ex_doc, "0.34.2", "13eedf3844ccdce25cfd837b99bea9ad92c4e511233199440488d217c92571e8", [:mix], [{:earmark_parser, "~> 1.4.39", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_c, ">= 0.1.0", [hex: :makeup_c, repo: "hexpm", optional: true]}, {:makeup_elixir, "~> 0.14 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1 or ~> 1.0", [hex: :makeup_erlang, repo: "hexpm", optional: false]}, {:makeup_html, ">= 0.1.0", [hex: :makeup_html, repo: "hexpm", optional: true]}], "hexpm", "5ce5f16b41208a50106afed3de6a2ed34f4acfd65715b82a0b84b49d995f95c1"},
"floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
"live_view_native": {:hex, :live_view_native, "0.3.0-rc.3", "0d8519d35b4e4d4ffc1f9b417790cc67b51e07275581aba27e55fea5a4727650", [:mix], [{:jason, "~> 1.2", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.20.10", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0.4", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: false]}, {:plug, "~> 1.15", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.5", [hex: :plug_cowboy, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.5", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "cfd30580073265358b66537340eb6e738718dc7c5778e6784c5c16e90ddc18b8"},
"live_view_native_stylesheet": {:hex, :live_view_native_stylesheet, "0.3.0-rc.3", "7fbee1e1e28627652ece7ed43d4cf341cb4bfe8c7dca3c29ced67cb695c16a46", [:mix], [{:live_view_native, "~> 0.3.0-rc.3", [hex: :live_view_native, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e1f6afe1755c5b3999d943763d8e0436c27fbcd655814883d0213fae72d47cdf"},
"live_view_native": {:git, "https://github.com/liveview-native/live_view_native.git", "0c9d98c1633c422ac8b90e6ca9bfee91d112973e", [branch: "main"]},
"live_view_native_stylesheet": {:git, "https://github.com/liveview-native/live_view_native_stylesheet.git", "05639ca9bb530dc16bbd5dc65cffe3d8014c82c2", [branch: "main"]},
"live_view_native_test": {:git, "https://github.com/liveview-native/live_view_native_test.git", "539ae931fa3936f3ee2f73ffa11f7100fe6554db", [branch: "main"]},
"makeup": {:hex, :makeup, "1.1.2", "9ba8837913bdf757787e71c1581c21f9d2455f4dd04cfca785c70bbfff1a76a3", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "cce1566b81fbcbd21eca8ffe808f33b221f9eee2cbc7a1706fc3da9ff18e6cac"},
"makeup_eex": {:hex, :makeup_eex, "0.1.2", "93a5ef3d28ed753215dba2d59cb40408b37cccb4a8205e53ef9b5319a992b700", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.16 or ~> 1.0", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_html, "~> 0.1.0 or ~> 1.0", [hex: :makeup_html, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "6140eafb28215ad7182282fd21d9aa6dcffbfbe0eb876283bc6b768a6c57b0c3"},
Expand Down
Loading
Loading