zf

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

default.ex (10670B)


      1 defmodule Credo.CLI.Command.Diff.Output.Default do
      2   @moduledoc false
      3 
      4   alias Credo.CLI.Command.Diff.DiffCommand
      5   alias Credo.CLI.Command.Diff.DiffSummary
      6   alias Credo.CLI.Filename
      7   alias Credo.CLI.Output
      8   alias Credo.CLI.Output.UI
      9   alias Credo.CLI.Sorter
     10   alias Credo.Execution
     11   alias Credo.Issue
     12   alias Credo.SourceFile
     13 
     14   @category_starting_order [:design, :readability, :refactor]
     15   @category_ending_order [:warning, :consistency, :custom, :unknown]
     16   @category_colors [
     17     design: :olive,
     18     readability: :blue,
     19     refactor: :yellow,
     20     warning: :red,
     21     consistency: :cyan
     22   ]
     23   @category_titles [
     24     design: "Software Design",
     25     readability: "Code Readability",
     26     refactor: "Refactoring opportunities",
     27     warning: "Warnings - please take a look",
     28     consistency: "Consistency"
     29   ]
     30   @many_source_files 60
     31   @per_category 5
     32   @indent 8
     33 
     34   @doc "Called before the analysis is run."
     35   def print_before_info(source_files, exec) do
     36     {_, git_ref_or_range} = DiffCommand.previous_ref(exec)
     37 
     38     case Enum.count(source_files) do
     39       0 ->
     40         UI.puts("No files found!")
     41 
     42       1 ->
     43         UI.puts([
     44           :faint,
     45           "Diffing 1 source file in working dir with ",
     46           :cyan,
     47           git_ref_or_range,
     48           :reset,
     49           :faint,
     50           " ..."
     51         ])
     52 
     53       count ->
     54         UI.puts([
     55           :faint,
     56           "Diffing #{count} source files in working dir with ",
     57           :cyan,
     58           git_ref_or_range,
     59           :reset,
     60           :faint,
     61           "#{checking_suffix(count)} ..."
     62         ])
     63     end
     64 
     65     Output.print_skipped_checks(exec)
     66   end
     67 
     68   defp checking_suffix(count) when count > @many_source_files do
     69     " (this might take a while)"
     70   end
     71 
     72   defp checking_suffix(_), do: ""
     73 
     74   @doc "Called after the analysis has run."
     75   def print_after_info(source_files, exec, time_load, time_run) do
     76     term_width = Output.term_columns()
     77     filtered_diff_markers = filtered_diff_markers(exec)
     78 
     79     issues_to_display =
     80       exec
     81       |> Execution.get_issues()
     82       |> Enum.filter(&Enum.member?(filtered_diff_markers, &1.diff_marker))
     83 
     84     categories =
     85       issues_to_display
     86       |> Enum.map(& &1.category)
     87       |> Enum.uniq()
     88 
     89     issue_map =
     90       Enum.into(categories, %{}, fn category ->
     91         {category, Enum.filter(issues_to_display, &(&1.category == category))}
     92       end)
     93 
     94     source_file_map = Enum.into(source_files, %{}, &{&1.filename, &1})
     95 
     96     categories
     97     |> Sorter.ensure(@category_starting_order, @category_ending_order)
     98     |> Enum.each(fn category ->
     99       print_issues_for_category(
    100         category,
    101         issue_map[category],
    102         source_file_map,
    103         exec,
    104         term_width
    105       )
    106     end)
    107 
    108     DiffSummary.print(source_files, exec, time_load, time_run)
    109   end
    110 
    111   defp print_issues_for_category(
    112          _category,
    113          nil,
    114          _source_file_map,
    115          _exec,
    116          _term_width
    117        ) do
    118     nil
    119   end
    120 
    121   defp print_issues_for_category(
    122          category,
    123          issues,
    124          source_file_map,
    125          exec,
    126          term_width
    127        ) do
    128     color = @category_colors[category] || :magenta
    129     title = @category_titles[category] || "Category: #{category}"
    130 
    131     UI.puts()
    132 
    133     [
    134       diff_marker(1, color),
    135       :bright,
    136       "#{color}_background" |> String.to_atom(),
    137       color,
    138       " ",
    139       Output.foreground_color(color),
    140       :normal,
    141       " #{title}" |> String.pad_trailing(term_width - 3)
    142     ]
    143     |> UI.puts()
    144 
    145     UI.puts([
    146       diff_marker(2, color),
    147       UI.edge(color)
    148     ])
    149 
    150     print_issues(issues, source_file_map, exec, term_width)
    151 
    152     if Enum.count(issues) > per_category(exec) do
    153       not_shown = Enum.count(issues) - per_category(exec)
    154 
    155       [
    156         diff_marker(),
    157         UI.edge(color),
    158         :faint,
    159         " ...  (#{not_shown} other new issues, use `--all` to show them)"
    160       ]
    161       |> UI.puts()
    162     end
    163   end
    164 
    165   defp print_issues(issues, source_file_map, exec, term_width) do
    166     count = per_category(exec)
    167     sort_weight = %{fixed: 0, old: 1, new: 2}
    168 
    169     issues
    170     |> Enum.sort_by(fn issue ->
    171       {sort_weight[issue.diff_marker], issue.priority, issue.severity, issue.filename,
    172        issue.line_no}
    173     end)
    174     |> Enum.reverse()
    175     |> Enum.take(count)
    176     |> do_print_issues(source_file_map, exec, term_width)
    177   end
    178 
    179   defp per_category(%Execution{all: true}), do: 1_000_000
    180   defp per_category(%Execution{all: false}), do: @per_category
    181 
    182   defp do_print_issues(
    183          issues,
    184          source_file_map,
    185          %Execution{format: _} = exec,
    186          term_width
    187        ) do
    188     Enum.each(issues, fn %Issue{filename: filename} = issue ->
    189       source_file = source_file_map[filename]
    190 
    191       do_print_issue(issue, source_file, exec, term_width)
    192     end)
    193   end
    194 
    195   defp do_print_issue(
    196          %Issue{
    197            check: check,
    198            message: message,
    199            filename: filename,
    200            priority: priority
    201          } = issue,
    202          source_file,
    203          %Execution{format: _, verbose: verbose} = exec,
    204          term_width
    205        ) do
    206     new_issue? = issue.diff_marker == :new
    207     fixed_issue? = issue.diff_marker == :fixed
    208 
    209     outer_color =
    210       if new_issue? do
    211         Output.check_color(issue)
    212       else
    213         [Output.check_color(issue), :faint]
    214       end
    215 
    216     inner_color =
    217       if new_issue? do
    218         Output.issue_color(issue)
    219       else
    220         [Output.issue_color(issue), :faint]
    221       end
    222 
    223     message_color = outer_color
    224     filename_color = :default_color
    225 
    226     tag_style =
    227       if outer_color == inner_color do
    228         :faint
    229       else
    230         :bright
    231       end
    232 
    233     message =
    234       if verbose do
    235         message <> " (" <> inspect(check) <> ")"
    236       else
    237         message
    238       end
    239 
    240     message
    241     |> UI.wrap_at(term_width - @indent)
    242     |> print_issue_message(
    243       issue,
    244       check,
    245       outer_color,
    246       message_color,
    247       tag_style,
    248       priority
    249     )
    250 
    251     location =
    252       if fixed_issue? do
    253         given_ref = Execution.get_assign(exec, "credo.diff.given_ref")
    254         previous_dirname = Execution.get_assign(exec, "credo.diff.previous_dirname")
    255 
    256         case given_ref do
    257           {:path, path} ->
    258             relative_filename = String.replace(filename, previous_dirname, "")
    259 
    260             "(dir:#{path}) #{relative_filename}"
    261 
    262           _ ->
    263             git_ref = Execution.get_assign(exec, "credo.diff.previous_git_ref")
    264 
    265             relative_filename =
    266               filename |> String.replace(previous_dirname, "") |> String.replace(~r/^[\/\\]/, "")
    267 
    268             "(git:#{git_ref}) #{relative_filename}"
    269         end
    270       else
    271         to_string(filename)
    272       end
    273 
    274     [
    275       diff_marker(issue.diff_marker),
    276       UI.edge(outer_color, @indent),
    277       filename_color,
    278       :faint,
    279       location,
    280       :default_color,
    281       :faint,
    282       Filename.pos_suffix(issue.line_no, issue.column),
    283       :conceal,
    284       " #",
    285       :reset,
    286       :faint,
    287       "(#{issue.scope})"
    288     ]
    289     |> UI.puts()
    290 
    291     if exec.verbose && issue.diff_marker == :new do
    292       print_issue_line(issue, source_file, inner_color, outer_color, term_width)
    293 
    294       [
    295         diff_marker(issue.diff_marker),
    296         UI.edge([
    297           outer_color,
    298           :faint
    299         ])
    300       ]
    301       |> UI.puts()
    302     end
    303   end
    304 
    305   defp print_issue_message(
    306          [first_line | other_lines],
    307          issue,
    308          check,
    309          outer_color,
    310          message_color,
    311          tag_style,
    312          priority
    313        ) do
    314     [
    315       diff_marker(issue.diff_marker),
    316       UI.edge(outer_color),
    317       outer_color,
    318       tag_style,
    319       Output.check_tag(check.category),
    320       " ",
    321       priority |> Output.priority_arrow(),
    322       :normal,
    323       message_color,
    324       " ",
    325       first_line
    326     ]
    327     |> UI.puts()
    328 
    329     other_lines
    330     |> Enum.each(&print_issue_message(&1, issue, outer_color, message_color))
    331   end
    332 
    333   defp print_issue_message(
    334          "",
    335          _issue,
    336          _outer_color,
    337          _message_color
    338        ) do
    339   end
    340 
    341   defp print_issue_message(
    342          message,
    343          issue,
    344          outer_color,
    345          message_color
    346        ) do
    347     [
    348       diff_marker(issue.diff_marker),
    349       UI.edge(outer_color),
    350       outer_color,
    351       String.duplicate(" ", @indent - 3),
    352       :normal,
    353       message_color,
    354       " ",
    355       message
    356     ]
    357     |> UI.puts()
    358   end
    359 
    360   defp print_issue_line(
    361          %Issue{line_no: nil},
    362          _source_file,
    363          _inner_color,
    364          _outer_color,
    365          _term_width
    366        ) do
    367     nil
    368   end
    369 
    370   defp print_issue_line(
    371          %Issue{} = issue,
    372          source_file,
    373          inner_color,
    374          outer_color,
    375          term_width
    376        ) do
    377     raw_line = SourceFile.line_at(source_file, issue.line_no)
    378     line = String.trim(raw_line)
    379 
    380     [diff_marker(issue.diff_marker), UI.edge([outer_color, :faint])]
    381     |> UI.puts()
    382 
    383     [
    384       diff_marker(issue.diff_marker),
    385       UI.edge([outer_color, :faint]),
    386       :cyan,
    387       :faint,
    388       String.duplicate(" ", @indent - 2),
    389       UI.truncate(line, term_width - @indent)
    390     ]
    391     |> UI.puts()
    392 
    393     print_issue_trigger_marker(issue, raw_line, inner_color, outer_color)
    394   end
    395 
    396   defp print_issue_trigger_marker(
    397          %Issue{column: nil},
    398          _line,
    399          _inner_color,
    400          _outer_color
    401        ) do
    402     nil
    403   end
    404 
    405   defp print_issue_trigger_marker(
    406          %Issue{} = issue,
    407          line,
    408          inner_color,
    409          outer_color
    410        ) do
    411     offset = String.length(line) - String.length(String.trim(line))
    412 
    413     # column is one-based
    414     x = max(issue.column - offset - 1, 0)
    415 
    416     w =
    417       case issue.trigger do
    418         nil -> 1
    419         atom -> atom |> to_string |> String.length()
    420       end
    421 
    422     [
    423       diff_marker(issue.diff_marker),
    424       UI.edge([outer_color, :faint], @indent),
    425       inner_color,
    426       String.duplicate(" ", x),
    427       :faint,
    428       String.duplicate("^", w)
    429     ]
    430     |> UI.puts()
    431   end
    432 
    433   defp filtered_diff_markers(exec) do
    434     filtered_diff_markers = [:new]
    435 
    436     filtered_diff_markers =
    437       if exec.cli_options.switches[:show_kept] do
    438         filtered_diff_markers ++ [:old]
    439       else
    440         filtered_diff_markers
    441       end
    442 
    443     if exec.cli_options.switches[:show_fixed] do
    444       filtered_diff_markers ++ [:fixed]
    445     else
    446       filtered_diff_markers
    447     end
    448   end
    449 
    450   defp diff_marker do
    451     [:faint, "  ", :reset, ""]
    452   end
    453 
    454   defp diff_marker(1, color) do
    455     [color, :faint, "  ", :reset, ""]
    456   end
    457 
    458   defp diff_marker(2, color) do
    459     [color, :faint, "  ", :reset, ""]
    460   end
    461 
    462   defp diff_marker(:new) do
    463     [:green, :bright, "+ ", :reset, ""]
    464   end
    465 
    466   defp diff_marker(:old) do
    467     [:faint, "~ ", :reset, ""]
    468   end
    469 
    470   defp diff_marker(:fixed) do
    471     [:faint, "✔ ", :reset, ""]
    472   end
    473 end