diff --git a/lib/preview_web/live/preview_live.ex b/lib/preview_web/live/preview_live.ex index d2ea562..ef7dfc2 100644 --- a/lib/preview_web/live/preview_live.ex +++ b/lib/preview_web/live/preview_live.ex @@ -1,7 +1,12 @@ defmodule PreviewWeb.PreviewLive do use PreviewWeb, :live_view + alias Makeup.Lexers.{EExLexer, ElixirLexer, ErlangLexer} + + require Logger + @max_file_size 2 * 1000 * 1000 + @makeup_timeout 1000 defmodule Exception do defexception [:plug_status] @@ -13,9 +18,10 @@ defmodule PreviewWeb.PreviewLive do @impl true def mount(params, _session, socket) do - version = params["version"] || Preview.Bucket.get_latest_version(params["package"]) + package = params["package"] + version = params["version"] || Preview.Bucket.get_latest_version(package) - if all_files = Preview.Bucket.get_file_list(params["package"], version) do + if all_files = Preview.Bucket.get_file_list(package, version) do all_files = Enum.sort(all_files) filename = @@ -25,19 +31,20 @@ defmodule PreviewWeb.PreviewLive do default_file(all_files) end - file_contents = file_contents_or_default(params["package"], version, filename) + file_contents = file_contents_or_default(package, version, filename) {:ok, assign(socket, - package: params["package"], + package: package, version: version, all_files: all_files, filename: filename, file_contents: file_contents, + makeup_file_contents: makeup_file_contents(package, version, filename, file_contents), selected_line: 0, page_title: filename, - meta_description: meta_description(params["package"], version, filename), - canonical: canonical_url(params["package"], filename) + meta_description: meta_description(package, version, filename), + canonical: canonical_url(package, filename) )} else raise Exception, plug_status: 404 @@ -75,6 +82,7 @@ defmodule PreviewWeb.PreviewLive do all_files: all_files, filename: filename, file_contents: file_contents, + makeup_file_contents: makeup_file_contents(package, version, filename, file_contents), page_title: filename, meta_description: meta_description(package, version, filename), canonical: canonical_url(package, filename) @@ -93,14 +101,40 @@ defmodule PreviewWeb.PreviewLive do if to_charlist(str) == io, do: [selected: "selected"], else: [] end - def print_file_contents(file_contents, filename) do - if makeup_supported?(filename) do - Makeup.highlight(file_contents) - else - content_tag(:pre, content_tag(:code, file_contents), class: "highlight") + defp makeup_file_contents(package, version, filename, file_contents) do + case makeup_lexer(filename) do + {:ok, lexer} -> + task = + Task.Supervisor.async_nolink(Preview.Tasks, fn -> + Makeup.highlight(file_contents, lexer: lexer) + end) + + case Task.yield(task, @makeup_timeout) || Task.shutdown(task, @makeup_timeout) do + {:ok, makeup} -> + makeup + + {:error, reason} -> + name = "#{package} #{version} #{filename}" + reason = inspect(reason) + Logger.warning("Failed to makeup #{name}, reason: #{reason}") + + default_content(file_contents) + + nil -> + name = "#{package} #{version} #{filename}" + Logger.warning("Failed to makeup #{name}, timeout") + default_content(file_contents) + end + + :error -> + default_content(file_contents) end end + defp default_content(file_contents) do + content_tag(:pre, content_tag(:code, file_contents), class: "highlight") + end + def default_file(all_files) do Enum.find(all_files, &(&1 |> String.downcase() |> String.starts_with?("readme"))) || Enum.find(all_files, &(&1 == "mix.exs")) || @@ -127,10 +161,15 @@ defmodule PreviewWeb.PreviewLive do end end - defp makeup_supported?(filename) do - Path.extname(filename) in [".ex", ".exs", ".erl", ".hrl", ".escript"] || - filename in ["rebar.config", "rebar.config.script"] || - String.ends_with?(filename, ".app.src") + defp makeup_lexer(filename) do + cond do + Path.extname(filename) in [".ex", ".exs"] -> {:ok, ElixirLexer} + Path.extname(filename) in [".eex", ".heex"] -> {:ok, EExLexer} + Path.extname(filename) in [".erl", ".hrl", ".escript"] -> {:ok, ErlangLexer} + filename in ["rebar.config", "rebar.config.script"] -> {:ok, ErlangLexer} + String.ends_with?(filename, ".app.src") -> {:ok, ErlangLexer} + true -> :error + end end defp maybe_assign_selected_line(<<"L", number::binary>>, socket) do diff --git a/lib/preview_web/live/preview_live.html.heex b/lib/preview_web/live/preview_live.html.heex index 69939bd..c935cf6 100644 --- a/lib/preview_web/live/preview_live.html.heex +++ b/lib/preview_web/live/preview_live.html.heex @@ -20,7 +20,7 @@
  • <% end %> - <%= raw(print_file_contents(@file_contents, @filename)) %> + <%= raw(@makeup_file_contents) %> <% end %> diff --git a/mix.exs b/mix.exs index f870aa0..1112427 100644 --- a/mix.exs +++ b/mix.exs @@ -37,6 +37,7 @@ defmodule Preview.MixProject do {:hex_core, "~> 0.8"}, {:jason, "~> 1.0"}, {:logster, "~> 1.0"}, + {:makeup_eex, "~> 0.1.2"}, {:makeup_elixir, "~> 0.16.0"}, {:makeup_erlang, "~> 0.1.0"}, {:makeup, "~> 1.0"}, diff --git a/mix.lock b/mix.lock index 0462fa9..dbcb8a9 100644 --- a/mix.lock +++ b/mix.lock @@ -23,16 +23,18 @@ "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, "jose": {:hex, :jose, "1.11.5", "3bc2d75ffa5e2c941ca93e5696b54978323191988eb8d225c2e663ddfefd515e", [:mix, :rebar3], [], "hexpm", "dcd3b215bafe02ea7c5b23dafd3eb8062a5cd8f2d904fd9caa323d37034ab384"}, "logster": {:hex, :logster, "1.1.0", "e4a11299473ea500adf534366ba53a4d8fbf64709e0e6e49fb16c85b1b23d91e", [:mix], [{:jason, "~> 1.1", [hex: :jason, repo: "hexpm", optional: false]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "875ea901c44d36b82d6f11ff344513faaa92fdf1aa212265e46296e9c78e2da3"}, - "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, - "makeup_elixir": {:hex, :makeup_elixir, "0.16.1", "cc9e3ca312f1cfeccc572b37a09980287e243648108384b97ff2b76e505c3555", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "e127a341ad1b209bd80f7bd1620a15693a9908ed780c3b763bccf7d200c767c6"}, - "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, + "makeup": {:hex, :makeup, "1.1.1", "fa0bc768698053b2b3869fa8a62616501ff9d11a562f3ce39580d60860c3a55e", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "5dc62fbdd0de44de194898b6710692490be74baa02d9d108bc29f007783b0b48"}, + "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"}, + "makeup_elixir": {:hex, :makeup_elixir, "0.16.2", "627e84b8e8bf22e60a2579dad15067c755531fea049ae26ef1020cad58fe9578", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "41193978704763f6bbe6cc2758b84909e62984c7752b3784bd3c218bb341706b"}, + "makeup_erlang": {:hex, :makeup_erlang, "0.1.5", "e0ff5a7c708dda34311f7522a8758e23bfcd7d8d8068dc312b5eb41c6fd76eba", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "94d2e986428585a21516d7d7149781480013c56e30c6a233534bedf38867a59a"}, + "makeup_html": {:hex, :makeup_html, "0.1.1", "c3d4abd39d5f7e925faca72ada6e9cc5c6f5fa7cd5bc0158315832656cf14d7f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "44f2a61bc5243645dd7fafeaa6cc28793cd22f3c76b861e066168f9a5b2c26a4"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, "mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "mint": {:hex, :mint, "1.5.1", "8db5239e56738552d85af398798c80648db0e90f343c8469f6c6d8898944fb6f", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "4a63e1e76a7c3956abd2c72f370a0d0aecddc3976dea5c27eccbecfa5e7d5b1e"}, "mox": {:hex, :mox, "1.0.2", "dc2057289ac478b35760ba74165b4b3f402f68803dd5aecd3bfd19c183815d64", [:mix], [], "hexpm", "f9864921b3aaf763c8741b5b8e6f908f44566f1e427b2630e89e9a73b981fef2"}, "nimble_options": {:hex, :nimble_options, "0.5.2", "42703307b924880f8c08d97719da7472673391905f528259915782bb346e0a1b", [:mix], [], "hexpm", "4da7f904b915fd71db549bcdc25f8d56f378ef7ae07dc1d372cbe72ba950dce0"}, - "nimble_parsec": {:hex, :nimble_parsec, "1.3.0", "9e18a119d9efc3370a3ef2a937bf0b24c088d9c4bf0ba9d7c3751d49d347d035", [:mix], [], "hexpm", "7977f183127a7cbe9346981e2f480dc04c55ffddaef746bd58debd566070eef8"}, + "nimble_parsec": {:hex, :nimble_parsec, "1.4.0", "51f9b613ea62cfa97b25ccc2c1b4216e81df970acd8e16e8d1bdc58fef21370d", [:mix], [], "hexpm", "9c565862810fb383e9838c1dd2d7d2c437b3d13b267414ba6af33e50d2d1cf28"}, "nimble_pool": {:hex, :nimble_pool, "1.0.0", "5eb82705d138f4dd4423f69ceb19ac667b3b492ae570c9f5c900bb3d2f50a847", [:mix], [], "hexpm", "80be3b882d2d351882256087078e1b1952a28bf98d0a287be87e4a24a710b67a"}, "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, "phoenix": {:hex, :phoenix, "1.7.2", "c375ffb482beb4e3d20894f84dd7920442884f5f5b70b9f4528cbe0cedefec63", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.4", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "1ebca94b32b4d0e097ab2444a9742ed8ff3361acad17365e4e6b2e79b4792159"},