zf

zenflows testing
git clone https://s.sonu.ch/~srfsh/zf.git
Log | Files | Refs | Submodules | README | LICENSE

default.ex (7607B)


      1 defmodule Credo.CLI.Command.Suggest.Output.Default do
      2   @moduledoc false
      3 
      4   alias Credo.CLI.Filename
      5   alias Credo.CLI.Output
      6   alias Credo.CLI.Output.Summary
      7   alias Credo.CLI.Output.UI
      8   alias Credo.CLI.Sorter
      9   alias Credo.Execution
     10   alias Credo.Issue
     11   alias Credo.SourceFile
     12 
     13   @category_starting_order [:design, :readability, :refactor]
     14   @category_ending_order [:warning, :consistency, :custom, :unknown]
     15   @category_colors [
     16     design: :olive,
     17     readability: :blue,
     18     refactor: :yellow,
     19     warning: :red,
     20     consistency: :cyan
     21   ]
     22   @category_titles [
     23     design: "Software Design",
     24     readability: "Code Readability",
     25     refactor: "Refactoring opportunities",
     26     warning: "Warnings - please take a look",
     27     consistency: "Consistency"
     28   ]
     29   @many_source_files 60
     30   @per_category 5
     31   @indent 8
     32 
     33   @doc "Called before the analysis is run."
     34   def print_before_info(source_files, exec) do
     35     case Enum.count(source_files) do
     36       0 ->
     37         UI.puts("No files found!")
     38 
     39       1 ->
     40         UI.puts("Checking 1 source file ...")
     41 
     42       count ->
     43         UI.puts("Checking #{count} source files#{checking_suffix(count)} ...")
     44     end
     45 
     46     Output.print_skipped_checks(exec)
     47   end
     48 
     49   defp checking_suffix(count) when count > @many_source_files do
     50     " (this might take a while)"
     51   end
     52 
     53   defp checking_suffix(_), do: ""
     54 
     55   @doc "Called after the analysis has run."
     56   def print_after_info(source_files, exec, time_load, time_run) do
     57     term_width = Output.term_columns()
     58 
     59     issues = Execution.get_issues(exec)
     60 
     61     categories =
     62       issues
     63       |> Enum.map(& &1.category)
     64       |> Enum.uniq()
     65 
     66     issue_map =
     67       Enum.into(categories, %{}, fn category ->
     68         {category, issues |> Enum.filter(&(&1.category == category))}
     69       end)
     70 
     71     source_file_map = Enum.into(source_files, %{}, &{&1.filename, &1})
     72 
     73     categories
     74     |> Sorter.ensure(@category_starting_order, @category_ending_order)
     75     |> Enum.each(fn category ->
     76       print_issues_for_category(
     77         category,
     78         issue_map[category],
     79         source_file_map,
     80         exec,
     81         term_width
     82       )
     83     end)
     84 
     85     source_files
     86     |> Summary.print(exec, time_load, time_run)
     87   end
     88 
     89   defp print_issues_for_category(
     90          _category,
     91          nil,
     92          _source_file_map,
     93          _exec,
     94          _term_width
     95        ) do
     96     nil
     97   end
     98 
     99   defp print_issues_for_category(
    100          category,
    101          issues,
    102          source_file_map,
    103          exec,
    104          term_width
    105        ) do
    106     color = @category_colors[category] || :magenta
    107     title = @category_titles[category] || "Category: #{category}"
    108 
    109     UI.puts()
    110 
    111     [
    112       :bright,
    113       "#{color}_background" |> String.to_atom(),
    114       color,
    115       " ",
    116       Output.foreground_color(color),
    117       :normal,
    118       " #{title}" |> String.pad_trailing(term_width - 1)
    119     ]
    120     |> UI.puts()
    121 
    122     color
    123     |> UI.edge()
    124     |> UI.puts()
    125 
    126     print_issues(issues, source_file_map, exec, term_width)
    127 
    128     if Enum.count(issues) > per_category(exec) do
    129       not_shown = Enum.count(issues) - per_category(exec)
    130 
    131       [
    132         UI.edge(color),
    133         :faint,
    134         " ...  (#{not_shown} more, use `--all` to show them)"
    135       ]
    136       |> UI.puts()
    137     end
    138   end
    139 
    140   defp print_issues(issues, source_file_map, exec, term_width) do
    141     count = per_category(exec)
    142 
    143     issues
    144     |> Enum.sort_by(fn issue ->
    145       {issue.priority, issue.severity, issue.filename, issue.line_no}
    146     end)
    147     |> Enum.reverse()
    148     |> Enum.take(count)
    149     |> do_print_issues(source_file_map, exec, term_width)
    150   end
    151 
    152   defp per_category(%Execution{all: true}), do: 1_000_000
    153   defp per_category(%Execution{all: false}), do: @per_category
    154 
    155   defp do_print_issues(
    156          issues,
    157          source_file_map,
    158          %Execution{format: _} = exec,
    159          term_width
    160        ) do
    161     Enum.each(issues, fn %Issue{filename: filename} = issue ->
    162       source_file = source_file_map[filename]
    163 
    164       do_print_issue(issue, source_file, exec, term_width)
    165     end)
    166   end
    167 
    168   defp do_print_issue(
    169          %Issue{
    170            check: check,
    171            message: message,
    172            filename: filename,
    173            priority: priority
    174          } = issue,
    175          source_file,
    176          %Execution{format: _, verbose: verbose} = exec,
    177          term_width
    178        ) do
    179     outer_color = Output.check_color(issue)
    180     inner_color = Output.issue_color(issue)
    181     message_color = outer_color
    182     filename_color = :default_color
    183 
    184     tag_style =
    185       if outer_color == inner_color do
    186         :faint
    187       else
    188         :bright
    189       end
    190 
    191     message =
    192       if verbose do
    193         message <> " [" <> inspect(check) <> "]"
    194       else
    195         message
    196       end
    197 
    198     message
    199     |> UI.wrap_at(term_width - @indent)
    200     |> print_issue_message(
    201       check,
    202       outer_color,
    203       message_color,
    204       tag_style,
    205       priority
    206     )
    207 
    208     [
    209       UI.edge(outer_color, @indent),
    210       filename_color,
    211       :faint,
    212       filename |> to_string,
    213       :default_color,
    214       :faint,
    215       Filename.pos_suffix(issue.line_no, issue.column),
    216       :conceal,
    217       " #",
    218       :reset,
    219       :faint,
    220       "(#{issue.scope})"
    221     ]
    222     |> UI.puts()
    223 
    224     if exec.verbose do
    225       print_issue_line(issue, source_file, inner_color, outer_color, term_width)
    226 
    227       UI.puts_edge([outer_color, :faint])
    228     end
    229   end
    230 
    231   defp print_issue_message(
    232          [first_line | other_lines],
    233          check,
    234          outer_color,
    235          message_color,
    236          tag_style,
    237          priority
    238        ) do
    239     [
    240       UI.edge(outer_color),
    241       outer_color,
    242       tag_style,
    243       Output.check_tag(check.category),
    244       " ",
    245       priority |> Output.priority_arrow(),
    246       :normal,
    247       message_color,
    248       " ",
    249       first_line
    250     ]
    251     |> UI.puts()
    252 
    253     other_lines
    254     |> Enum.each(&print_issue_message(&1, outer_color, message_color))
    255   end
    256 
    257   defp print_issue_message("", _outer_color, _message_color) do
    258   end
    259 
    260   defp print_issue_message(message, outer_color, message_color) do
    261     [
    262       UI.edge(outer_color),
    263       outer_color,
    264       String.duplicate(" ", @indent - 3),
    265       :normal,
    266       message_color,
    267       " ",
    268       message
    269     ]
    270     |> UI.puts()
    271   end
    272 
    273   defp print_issue_line(
    274          %Issue{line_no: nil},
    275          _source_file,
    276          _inner_color,
    277          _outer_color,
    278          _term_width
    279        ) do
    280     nil
    281   end
    282 
    283   defp print_issue_line(
    284          %Issue{} = issue,
    285          source_file,
    286          inner_color,
    287          outer_color,
    288          term_width
    289        ) do
    290     raw_line = SourceFile.line_at(source_file, issue.line_no)
    291     line = String.trim(raw_line)
    292 
    293     [outer_color, :faint]
    294     |> UI.edge()
    295     |> UI.puts()
    296 
    297     [
    298       UI.edge([outer_color, :faint]),
    299       :cyan,
    300       :faint,
    301       String.duplicate(" ", @indent - 2),
    302       UI.truncate(line, term_width - @indent)
    303     ]
    304     |> UI.puts()
    305 
    306     print_issue_trigger_marker(issue, raw_line, inner_color, outer_color)
    307   end
    308 
    309   defp print_issue_trigger_marker(
    310          %Issue{column: nil},
    311          _line,
    312          _inner_color,
    313          _outer_color
    314        ) do
    315     nil
    316   end
    317 
    318   defp print_issue_trigger_marker(
    319          %Issue{} = issue,
    320          line,
    321          inner_color,
    322          outer_color
    323        ) do
    324     offset = String.length(line) - String.length(String.trim(line))
    325 
    326     # column is one-based
    327     x = max(issue.column - offset - 1, 0)
    328 
    329     w =
    330       case issue.trigger do
    331         nil -> 1
    332         atom -> atom |> to_string |> String.length()
    333       end
    334 
    335     [
    336       UI.edge([outer_color, :faint], @indent),
    337       inner_color,
    338       String.duplicate(" ", x),
    339       :faint,
    340       String.duplicate("^", w)
    341     ]
    342     |> UI.puts()
    343   end
    344 end