defmodule Credo.CLI.Command.List.Output.Default do @moduledoc false alias Credo.CLI.Filename alias Credo.CLI.Output alias Credo.CLI.Output.Summary alias Credo.CLI.Output.UI alias Credo.Code.Scope alias Credo.Issue alias Credo.SourceFile @indent 8 @doc "Called before the analysis is run." def print_before_info(source_files, exec) do UI.puts("") case Enum.count(source_files) do 0 -> UI.puts("No files found!") 1 -> UI.puts("Checking 1 source file ...") count -> UI.puts("Checking #{count} source files ...") end Output.print_skipped_checks(exec) end @doc "Called after the analysis has run." def print_after_info(source_files, exec, time_load, time_run) do term_width = Output.term_columns() source_files |> Enum.sort_by(& &1.filename) |> Enum.each(&print_issues(&1, exec, term_width)) source_files |> Summary.print(exec, time_load, time_run) end defp print_issues( %SourceFile{filename: filename} = source_file, exec, term_width ) do issues = Credo.Execution.get_issues(exec, filename) issues |> print_issues(filename, source_file, exec, term_width) end defp print_issues(issues, _filename, source_file, _exec, term_width) do if issues |> Enum.any?() do first_issue = issues |> List.first() scope_name = Scope.mod_name(first_issue.scope) color = Output.check_color(first_issue) UI.puts() [ :bright, "#{color}_background" |> String.to_atom(), color, " ", Output.foreground_color(color), :normal, " #{scope_name}" |> String.pad_trailing(term_width - 1) ] |> UI.puts() color |> UI.edge() |> UI.puts() issues |> Enum.sort_by(& &1.line_no) |> Enum.each(&print_issue(&1, source_file, term_width)) end end defp print_issue( %Issue{ check: check, message: message, filename: filename, priority: priority } = issue, source_file, term_width ) do outer_color = Output.check_color(issue) inner_color = Output.issue_color(issue) message_color = inner_color filename_color = :default_color tag_style = if outer_color == inner_color do :faint else :bright end [ UI.edge(outer_color), inner_color, tag_style, Output.check_tag(check.category), " ", priority |> Output.priority_arrow(), :normal, message_color, " ", message ] |> UI.puts() [ UI.edge(outer_color, @indent), filename_color, :faint, filename |> to_string, :default_color, :faint, Filename.pos_suffix(issue.line_no, issue.column), :faint, " (#{issue.scope})" ] |> UI.puts() if issue.line_no do print_issue_line_no(source_file, term_width, issue, outer_color, inner_color) end UI.puts_edge([outer_color, :faint], @indent) end defp print_issue_column(raw_line, line, issue, outer_color, inner_color) do offset = String.length(raw_line) - String.length(line) # column is one-based x = max(issue.column - offset - 1, 0) w = if is_nil(issue.trigger) do 1 else issue.trigger |> to_string() |> String.length() end [ UI.edge([outer_color, :faint], @indent), inner_color, String.duplicate(" ", x), :faint, String.duplicate("^", w) ] |> UI.puts() end defp print_issue_line_no(source_file, term_width, issue, outer_color, inner_color) do raw_line = SourceFile.line_at(source_file, issue.line_no) line = String.trim(raw_line) UI.puts_edge([outer_color, :faint]) [ UI.edge([outer_color, :faint]), :cyan, :faint, String.duplicate(" ", @indent - 2), UI.truncate(line, term_width - @indent) ] |> UI.puts() if issue.column, do: print_issue_column(raw_line, line, issue, outer_color, inner_color) end end