zf

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

default.ex (12638B)


      1 defmodule Credo.CLI.Command.Explain.Output.Default do
      2   @moduledoc false
      3 
      4   alias Credo.CLI.Output
      5   alias Credo.CLI.Output.UI
      6   alias Credo.Code.Scope
      7 
      8   @indent 8
      9   @params_min_indent 10
     10 
     11   @doc "Called before the analysis is run."
     12   def print_before_info(source_files, exec) do
     13     UI.puts()
     14 
     15     case Enum.count(source_files) do
     16       0 -> UI.puts("No files found!")
     17       1 -> UI.puts("Checking 1 source file ...")
     18       count -> UI.puts("Checking #{count} source files ...")
     19     end
     20 
     21     Output.print_skipped_checks(exec)
     22   end
     23 
     24   @doc "Called after the analysis has run."
     25   def print_after_info(explanations, exec, nil, nil) do
     26     term_width = Output.term_columns()
     27 
     28     print_explanations_for_check(explanations, exec, term_width)
     29   end
     30 
     31   def print_after_info(explanations, exec, line_no, column) do
     32     term_width = Output.term_columns()
     33 
     34     print_explanations_for_issue(explanations, exec, term_width, line_no, column)
     35   end
     36 
     37   #
     38   # CHECK
     39   #
     40 
     41   defp print_explanations_for_check(explanations, _exec, term_width) do
     42     Enum.each(explanations, &print_check(&1, term_width))
     43   end
     44 
     45   defp print_check(
     46          %{
     47            category: category,
     48            check: check,
     49            explanation_for_issue: explanation_for_issue,
     50            priority: priority
     51          },
     52          term_width
     53        ) do
     54     check_name = check |> to_string |> String.replace(~r/^Elixir\./, "")
     55     color = Output.check_color(check.category)
     56 
     57     UI.puts()
     58 
     59     [
     60       :bright,
     61       "#{color}_background" |> String.to_atom(),
     62       color,
     63       " ",
     64       Output.foreground_color(color),
     65       :normal,
     66       " Check: #{check_name}" |> String.pad_trailing(term_width - 1)
     67     ]
     68     |> UI.puts()
     69 
     70     UI.puts_edge(color)
     71 
     72     outer_color = Output.check_color(category)
     73     inner_color = Output.check_color(category)
     74 
     75     tag_style =
     76       if outer_color == inner_color do
     77         :faint
     78       else
     79         :bright
     80       end
     81 
     82     [
     83       UI.edge(outer_color),
     84       inner_color,
     85       tag_style,
     86       "  ",
     87       Output.check_tag(check.category),
     88       :reset,
     89       " Category: #{check.category} "
     90     ]
     91     |> UI.puts()
     92 
     93     [
     94       UI.edge(outer_color),
     95       inner_color,
     96       tag_style,
     97       "   ",
     98       priority |> Output.priority_arrow(),
     99       :reset,
    100       "  Priority: #{Output.priority_name(priority)} "
    101     ]
    102     |> UI.puts()
    103 
    104     UI.puts_edge(outer_color)
    105 
    106     UI.puts_edge([outer_color, :faint], @indent)
    107 
    108     print_check_explanation(explanation_for_issue, outer_color)
    109     print_params_explanation(check, outer_color)
    110 
    111     UI.puts_edge([outer_color, :faint])
    112   end
    113 
    114   #
    115   # ISSUE
    116   #
    117 
    118   defp print_explanations_for_issue(
    119          [],
    120          _exec,
    121          _term_width,
    122          _line_no,
    123          _column
    124        ) do
    125     nil
    126   end
    127 
    128   defp print_explanations_for_issue(
    129          explanations,
    130          _exec,
    131          term_width,
    132          _line_no,
    133          _column
    134        ) do
    135     first_explanation = explanations |> List.first()
    136     scope_name = Scope.mod_name(first_explanation.scope)
    137     color = Output.check_color(first_explanation.category)
    138 
    139     UI.puts()
    140 
    141     [
    142       :bright,
    143       "#{color}_background" |> String.to_atom(),
    144       color,
    145       " ",
    146       Output.foreground_color(color),
    147       :normal,
    148       " #{scope_name}" |> String.pad_trailing(term_width - 1)
    149     ]
    150     |> UI.puts()
    151 
    152     UI.puts_edge(color)
    153 
    154     Enum.each(explanations, &print_issue(&1, term_width))
    155   end
    156 
    157   defp print_issue(
    158          %{
    159            category: category,
    160            check: check,
    161            column: column,
    162            explanation_for_issue: explanation_for_issue,
    163            filename: filename,
    164            trigger: trigger,
    165            line_no: line_no,
    166            message: message,
    167            priority: priority,
    168            related_code: related_code,
    169            scope: scope
    170          },
    171          term_width
    172        ) do
    173     pos = pos_string(line_no, column)
    174 
    175     outer_color = Output.check_color(category)
    176     inner_color = Output.check_color(category)
    177     message_color = inner_color
    178     filename_color = :default_color
    179 
    180     tag_style =
    181       if outer_color == inner_color do
    182         :faint
    183       else
    184         :bright
    185       end
    186 
    187     [
    188       UI.edge(outer_color),
    189       inner_color,
    190       tag_style,
    191       "  ",
    192       Output.check_tag(check.category),
    193       :reset,
    194       " Category: #{check.category} "
    195     ]
    196     |> UI.puts()
    197 
    198     [
    199       UI.edge(outer_color),
    200       inner_color,
    201       tag_style,
    202       "   ",
    203       priority |> Output.priority_arrow(),
    204       :reset,
    205       "  Priority: #{Output.priority_name(priority)} "
    206     ]
    207     |> UI.puts()
    208 
    209     UI.puts_edge(outer_color)
    210 
    211     [
    212       UI.edge(outer_color),
    213       inner_color,
    214       tag_style,
    215       "    ",
    216       :normal,
    217       message_color,
    218       "  ",
    219       message
    220     ]
    221     |> UI.puts()
    222 
    223     [
    224       UI.edge(outer_color, @indent),
    225       filename_color,
    226       :faint,
    227       to_string(filename),
    228       :default_color,
    229       :faint,
    230       pos,
    231       :faint,
    232       " (#{scope})"
    233     ]
    234     |> UI.puts()
    235 
    236     if line_no do
    237       print_issue_line_no(
    238         term_width,
    239         line_no,
    240         column,
    241         trigger,
    242         related_code,
    243         outer_color,
    244         inner_color
    245       )
    246     end
    247 
    248     UI.puts_edge([outer_color, :faint], @indent)
    249 
    250     print_check_explanation(explanation_for_issue, outer_color)
    251     print_params_explanation(check, outer_color)
    252 
    253     UI.puts_edge([outer_color, :faint])
    254   end
    255 
    256   def print_check_explanation(explanation_for_issue, outer_color) do
    257     [
    258       UI.edge([outer_color, :faint]),
    259       :reset,
    260       :color239,
    261       String.duplicate(" ", @indent - 5),
    262       "__ WHY IT MATTERS"
    263     ]
    264     |> UI.puts()
    265 
    266     UI.puts_edge([outer_color, :faint])
    267 
    268     (explanation_for_issue || "TODO: Insert explanation")
    269     |> String.trim()
    270     |> String.split("\n")
    271     |> Enum.flat_map(&format_explanation(&1, outer_color))
    272     |> Enum.slice(0..-2)
    273     |> UI.puts()
    274 
    275     UI.puts_edge([outer_color, :faint])
    276   end
    277 
    278   def format_explanation(line, outer_color) do
    279     [
    280       UI.edge([outer_color, :faint], @indent),
    281       :reset,
    282       line |> format_explanation_text,
    283       "\n"
    284     ]
    285   end
    286 
    287   def format_explanation_text("    " <> line) do
    288     [:yellow, :faint, "    ", line]
    289   end
    290 
    291   def format_explanation_text(line) do
    292     # TODO: format things in backticks in help texts
    293     # case Regex.run(~r/(\`[a-zA-Z_\.]+\`)/, line) do
    294     #  v ->
    295     #    # IO.inspect(v)
    296     [:reset, line]
    297     # end
    298   end
    299 
    300   defp pos_string(nil, nil), do: ""
    301   defp pos_string(line_no, nil), do: ":#{line_no}"
    302   defp pos_string(line_no, column), do: ":#{line_no}:#{column}"
    303 
    304   def print_params_explanation(nil, _), do: nil
    305 
    306   def print_params_explanation(check, outer_color) do
    307     check_name = check |> to_string |> String.replace(~r/^Elixir\./, "")
    308 
    309     [
    310       UI.edge([outer_color, :faint]),
    311       :reset,
    312       :color239,
    313       String.duplicate(" ", @indent - 5),
    314       "__ CONFIGURATION OPTIONS"
    315     ]
    316     |> UI.puts()
    317 
    318     UI.puts_edge([outer_color, :faint])
    319 
    320     print_params_explanation(
    321       outer_color,
    322       check_name,
    323       check.explanations()[:params],
    324       check.param_defaults()
    325     )
    326   end
    327 
    328   def print_params_explanation(outer_color, check_name, param_explanations, _defaults)
    329       when param_explanations in [nil, []] do
    330     [
    331       UI.edge([outer_color, :faint]),
    332       :reset,
    333       String.duplicate(" ", @indent - 2),
    334       "You can disable this check by using this tuple"
    335     ]
    336     |> UI.puts()
    337 
    338     UI.puts_edge([outer_color, :faint])
    339 
    340     [
    341       UI.edge([outer_color, :faint]),
    342       :reset,
    343       String.duplicate(" ", @indent - 2),
    344       "  {",
    345       :cyan,
    346       check_name,
    347       :reset,
    348       ", ",
    349       :cyan,
    350       "false",
    351       :reset,
    352       "}"
    353     ]
    354     |> UI.puts()
    355 
    356     UI.puts_edge([outer_color, :faint])
    357 
    358     [
    359       UI.edge([outer_color, :faint]),
    360       :reset,
    361       String.duplicate(" ", @indent - 2),
    362       "There are no other configuration options."
    363     ]
    364     |> UI.puts()
    365 
    366     UI.puts_edge([outer_color, :faint])
    367   end
    368 
    369   def print_params_explanation(outer_color, check_name, keywords, defaults) do
    370     [
    371       UI.edge([outer_color, :faint]),
    372       :reset,
    373       String.duplicate(" ", @indent - 2),
    374       "To configure this check, use this tuple"
    375     ]
    376     |> UI.puts()
    377 
    378     UI.puts_edge([outer_color, :faint])
    379 
    380     [
    381       UI.edge([outer_color, :faint]),
    382       :reset,
    383       String.duplicate(" ", @indent - 2),
    384       "  {",
    385       :cyan,
    386       check_name,
    387       :reset,
    388       ", ",
    389       :cyan,
    390       :faint,
    391       "<params>",
    392       :reset,
    393       "}"
    394     ]
    395     |> UI.puts()
    396 
    397     UI.puts_edge([outer_color, :faint])
    398 
    399     [
    400       UI.edge([outer_color, :faint]),
    401       :reset,
    402       String.duplicate(" ", @indent - 2),
    403       "with ",
    404       :cyan,
    405       :faint,
    406       "<params>",
    407       :reset,
    408       " being ",
    409       :cyan,
    410       "false",
    411       :reset,
    412       " or any combination of these keywords:"
    413     ]
    414     |> UI.puts()
    415 
    416     UI.puts_edge([outer_color, :faint])
    417 
    418     params_indent = get_params_indent(keywords, @params_min_indent)
    419 
    420     keywords
    421     |> Enum.each(fn {param, text} ->
    422       [head | tail] = String.split(text, "\n")
    423 
    424       [
    425         UI.edge([outer_color, :faint]),
    426         :reset,
    427         String.duplicate(" ", @indent - 2),
    428         :cyan,
    429         "  #{param}:" |> String.pad_trailing(params_indent + 3),
    430         :reset,
    431         head
    432       ]
    433       |> UI.puts()
    434 
    435       tail
    436       |> List.wrap()
    437       |> Enum.each(fn line ->
    438         [
    439           UI.edge([outer_color, :faint]),
    440           :reset,
    441           String.duplicate(" ", @indent - 2),
    442           :cyan,
    443           String.pad_trailing("", params_indent + 3),
    444           :reset,
    445           line
    446         ]
    447         |> UI.puts()
    448       end)
    449 
    450       default = defaults[param]
    451 
    452       if default do
    453         default_text = "(defaults to #{inspect(default)})"
    454 
    455         [
    456           UI.edge([outer_color, :faint]),
    457           :reset,
    458           String.duplicate(" ", @indent - 2),
    459           :cyan,
    460           " " |> String.pad_trailing(params_indent + 3),
    461           :reset,
    462           :faint,
    463           default_text
    464         ]
    465         |> UI.puts()
    466       end
    467     end)
    468   end
    469 
    470   defp get_params_indent(keywords, min_indent) do
    471     params_indent =
    472       Enum.reduce(keywords, min_indent, fn {param, _text}, current ->
    473         size =
    474           param
    475           |> to_string
    476           |> String.length()
    477 
    478         if size > current do
    479           size
    480         else
    481           current
    482         end
    483       end)
    484 
    485     # Round up to the next multiple of 2
    486     (trunc(params_indent / 2) + 1) * 2
    487   end
    488 
    489   defp print_issue_column(column, trigger, outer_color, inner_color) do
    490     offset = 0
    491     # column is one-based
    492     x = max(column - offset - 1, 0)
    493 
    494     w =
    495       if is_nil(trigger) do
    496         1
    497       else
    498         trigger
    499         |> to_string()
    500         |> String.length()
    501       end
    502 
    503     [
    504       UI.edge([outer_color, :faint], @indent),
    505       inner_color,
    506       String.duplicate(" ", x),
    507       :faint,
    508       String.duplicate("^", w)
    509     ]
    510     |> UI.puts()
    511   end
    512 
    513   defp print_issue_line_no(
    514          term_width,
    515          line_no,
    516          column,
    517          trigger,
    518          related_code,
    519          outer_color,
    520          inner_color
    521        ) do
    522     UI.puts_edge([outer_color, :faint])
    523 
    524     [
    525       UI.edge([outer_color, :faint]),
    526       :reset,
    527       :color239,
    528       String.duplicate(" ", @indent - 5),
    529       "__ CODE IN QUESTION"
    530     ]
    531     |> UI.puts()
    532 
    533     UI.puts_edge([outer_color, :faint])
    534 
    535     code_color = :faint
    536 
    537     print_source_line(
    538       related_code,
    539       line_no - 2,
    540       term_width,
    541       code_color,
    542       outer_color
    543     )
    544 
    545     print_source_line(
    546       related_code,
    547       line_no - 1,
    548       term_width,
    549       code_color,
    550       outer_color
    551     )
    552 
    553     print_source_line(
    554       related_code,
    555       line_no,
    556       term_width,
    557       [:cyan, :bright],
    558       outer_color
    559     )
    560 
    561     if column, do: print_issue_column(column, trigger, outer_color, inner_color)
    562 
    563     print_source_line(
    564       related_code,
    565       line_no + 1,
    566       term_width,
    567       code_color,
    568       outer_color
    569     )
    570 
    571     print_source_line(
    572       related_code,
    573       line_no + 2,
    574       term_width,
    575       code_color,
    576       outer_color
    577     )
    578   end
    579 
    580   defp print_source_line(related_code, line_no, term_width, code_color, outer_color) do
    581     line =
    582       related_code
    583       |> Enum.find_value(fn
    584         {line_no2, line} when line_no2 == line_no -> line
    585         _ -> nil
    586       end)
    587 
    588     if line do
    589       line_no_str =
    590         "#{line_no} "
    591         |> String.pad_leading(@indent - 2)
    592 
    593       [
    594         UI.edge([outer_color, :faint]),
    595         :reset,
    596         :faint,
    597         line_no_str,
    598         :reset,
    599         code_color,
    600         UI.truncate(line, term_width - @indent)
    601       ]
    602       |> UI.puts()
    603     end
    604   end
    605 end