defmodule Credo.CLI.Command.ReportCard.Output.Html do @moduledoc false alias Credo.Execution def print_before_info(_source_files, _exec), do: nil def print_after_info(_source_files, exec, _time_load, _time_run) do exec |> Execution.get_issues() |> print_issues() end alias Credo.Issue alias Credo.Service.SourceFileLines @head_links """ """ @inline_styles """ """ @table_scripts """ """ @inline_scripts """ """ def print_issues(issues) do issues |> Enum.reduce(Map.new(), &Credo.CLI.Output.Formatter.ReportCard.categorize_module/2) |> write_html() end defp write_html(issue_map) do File.mkdir("credo") {:ok, f} = File.open("credo/index.html", [:binary, :write]) IO.binwrite(f, [ "\n\n\nCredo Report\n", @head_links, @inline_styles, @inline_scripts, @table_scripts, "\n\n", "
\n", "
\n" ]) IO.binwrite(f, "

Credo Module Scores

") IO.binwrite(f, [ "\n", "\n\n", "\n", "\n", "\n", "", "\n\n\n\n" ]) issue_map |> Enum.sort_by(fn {k, _} -> k end) |> Enum.each(fn {k, v} -> module_template(f, k, v) end) IO.binwrite(f, [ "\n
FileGradeIssuesRemediation Time
\n", "
\n
\n\n" ]) File.close(f) end defp module_template(io, k, v) do {issue_count, raw_score} = Credo.CLI.Output.Formatter.ReportCard.score_issues(k, v) mod_grade = Credo.CLI.Output.Formatter.ReportCard.grade(raw_score) dir_path = Path.join("credo", Path.dirname(k)) file_path = Path.join("credo", k <> ".html") link_path = k <> ".html" IO.binwrite(io, [ "\n", "", h(k), "\n\n", "#{mod_grade}", "#{issue_count}", "", Credo.CLI.Output.Formatter.ReportCard.format_remediation_time(raw_score), "", "\n" ]) File.mkdir_p(dir_path) {:ok, f} = File.open(file_path, [:binary, :write]) IO.binwrite(f, [ "\n\n\n", h(k), "\n", @head_links, @inline_styles, @inline_scripts, @table_scripts, "\n\n", "
\n", "
\n", "

", h(k), "

", "", "
\n
\n\n" ]) File.close(f) end defp format_smell(f, issue) do {target_line, min_line, max_line} = line_indexes(issue) IO.binwrite(f, "
  • \n") IO.binwrite(f, ["

    ", h(issue.message), "

    "]) IO.binwrite(f, [ "
    \n", "
    ",
          line_content(issue, min_line, max_line),
          "\n
    \n
    \n", "
    \nDetails\n" ]) IO.binwrite(f, format_explanation(issue)) IO.binwrite(f, "
    \n
  • \n") end defp format_explanation(issue) do Enum.map(String.split(issue.check.explanation, "\n\n"), fn sv -> case is_source_explanation(sv) do false -> "

    " <> h(sv) <> "

    \n\n" _ -> "
    " <> h(sv) <> "
    \n\n" end end) end defp is_source_explanation(<<" ", _::binary>>) do true end defp is_source_explanation(_) do false end defp line_indexes( %Issue{check: Credo.Check.Warning.ExCoverallsUncovered, severity: sev} = issue ) do exact_line = issue.line_no || 1 last_line = exact_line + sev - 1 first_line = minimum_line(exact_line) {:ok, src_lines} = SourceFileLines.get(issue.filename) keys = :proplists.get_keys(src_lines) line_pointer = case sev > 1 do false -> exact_line _ -> "#{exact_line}-#{last_line}" end {line_pointer, first_line, max_line(last_line, keys)} end defp line_indexes(issue) do exact_line = issue.line_no || 1 first_line = minimum_line(exact_line) {:ok, src_lines} = SourceFileLines.get(issue.filename) keys = :proplists.get_keys(src_lines) {exact_line, first_line, max_line(exact_line, keys)} end defp line_content(issue, min, max) do {:ok, src_lines} = SourceFileLines.get(issue.filename) Enum.join( Enum.map(Range.new(min, max), fn i -> h(:proplists.get_value(i, src_lines)) end), "\n" ) <> "\n" end defp max_line(target_line, keys) do case Enum.member?(keys, target_line + 2) do false -> case Enum.member?(keys, target_line + 1) do false -> target_line _ -> target_line + 1 end _ -> target_line + 2 end end def minimum_line(2), do: 1 def minimum_line(1), do: 1 def minimum_line(n), do: n - 2 defp h(binary) do html_escape(binary, <<>>) end defp html_escape(<<>>, str) do str end defp html_escape(<<"'", rest::binary>>, str) do html_escape(rest, str <> "'") end defp html_escape(<<"\"", rest::binary>>, str) do html_escape(rest, str <> """) end defp html_escape(<<"<", rest::binary>>, str) do html_escape(rest, str <> "<") end defp html_escape(<<">", rest::binary>>, str) do html_escape(rest, str <> ">") end defp html_escape(<>, str) do html_escape(rest, str <> <>) end end