zf

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

token.ex (8114B)


      1 defmodule Credo.Code.Token do
      2   @moduledoc """
      3   This module provides helper functions to analyse tokens returned by `Credo.Code.to_tokens/1`.
      4   """
      5 
      6   @doc """
      7   Returns `true` if the given `token` contains a line break.
      8   """
      9   def eol?(token)
     10 
     11   def eol?(list) when is_list(list) do
     12     Enum.any?(list, &eol?/1)
     13   end
     14 
     15   def eol?({_, {_, _, _}, _, list, _, _}) when is_list(list) do
     16     Enum.any?(list, &eol?/1)
     17   end
     18 
     19   def eol?({_, {_, _, _}, list}) when is_list(list) do
     20     Enum.any?(list, &eol?/1)
     21   end
     22 
     23   def eol?({{_, _, _}, list}) when is_list(list) do
     24     Enum.any?(list, &eol?/1)
     25   end
     26 
     27   def eol?({:eol, {_, _, _}}), do: true
     28   def eol?(_), do: false
     29 
     30   @doc """
     31   Returns the position of a token in the form
     32 
     33       {line_no_start, col_start, line_no_end, col_end}
     34   """
     35   def position(token)
     36 
     37   def position({_, {line_no, col_start, _}, atom_or_charlist, _, _, _}) do
     38     position_tuple(atom_or_charlist, line_no, col_start)
     39   end
     40 
     41   def position({_, {line_no, col_start, _}, atom_or_charlist, _, _}) do
     42     position_tuple(atom_or_charlist, line_no, col_start)
     43   end
     44 
     45   def position({_, {line_no, col_start, _}, atom_or_charlist, _}) do
     46     position_tuple(atom_or_charlist, line_no, col_start)
     47   end
     48 
     49   def position({:bin_string, {line_no, col_start, _}, atom_or_charlist}) do
     50     position_tuple_for_quoted_string(atom_or_charlist, line_no, col_start)
     51   end
     52 
     53   def position({:list_string, {line_no, col_start, _}, atom_or_charlist}) do
     54     position_tuple_for_quoted_string(atom_or_charlist, line_no, col_start)
     55   end
     56 
     57   def position({:bin_heredoc, {line_no, col_start, _}, atom_or_charlist}) do
     58     position_tuple_for_heredoc(atom_or_charlist, line_no, col_start)
     59   end
     60 
     61   def position({:list_heredoc, {line_no, col_start, _}, atom_or_charlist}) do
     62     position_tuple_for_heredoc(atom_or_charlist, line_no, col_start)
     63   end
     64 
     65   def position({:atom_unsafe, {line_no, col_start, _}, atom_or_charlist}) do
     66     position_tuple_for_quoted_string(atom_or_charlist, line_no, col_start)
     67   end
     68 
     69   # Elixir >= 1.10.0 tuple syntax
     70   def position({:sigil, {line_no, col_start, nil}, _, atom_or_charlist, _list, _number, _binary}) do
     71     position_tuple_for_quoted_string(atom_or_charlist, line_no, col_start)
     72   end
     73 
     74   # Elixir >= 1.9.0 tuple syntax
     75   def position({{line_no, col_start, nil}, {_line_no2, _col_start2, nil}, atom_or_charlist}) do
     76     position_tuple_for_quoted_string(atom_or_charlist, line_no, col_start)
     77   end
     78 
     79   def position({:kw_identifier_unsafe, {line_no, col_start, _}, atom_or_charlist}) do
     80     position_tuple_for_quoted_string(atom_or_charlist, line_no, col_start)
     81   end
     82 
     83   # Elixir < 1.9.0 tuple syntax
     84   def position({_, {line_no, col_start, _}, atom_or_charlist}) do
     85     position_tuple(atom_or_charlist, line_no, col_start)
     86   end
     87 
     88   def position({atom_or_charlist, {line_no, col_start, _}}) do
     89     position_tuple(atom_or_charlist, line_no, col_start)
     90   end
     91 
     92   # interpolation
     93   def position({{line_no, col_start, _}, list}) when is_list(list) do
     94     {line_no, col_start, line_no_end, col_end} =
     95       position_tuple_for_quoted_string(list, line_no, col_start)
     96 
     97     {line_no, col_start, line_no_end, col_end}
     98   end
     99 
    100   #
    101 
    102   defp position_tuple(list, line_no, col_start) when is_list(list) do
    103     binary = to_string(list)
    104     col_end = col_start + String.length(binary)
    105 
    106     {line_no, col_start, line_no, col_end}
    107   end
    108 
    109   defp position_tuple(atom, line_no, col_start) when is_atom(atom) do
    110     binary = to_string(atom)
    111     col_end = col_start + String.length(binary)
    112 
    113     {line_no, col_start, line_no, col_end}
    114   end
    115 
    116   defp position_tuple(number, line_no, col_start) when is_number(number) do
    117     binary = to_string([number])
    118     col_end = col_start + String.length(binary)
    119 
    120     {line_no, col_start, line_no, col_end}
    121   end
    122 
    123   defp position_tuple(_, _line_no, _col_start), do: nil
    124 
    125   defp position_tuple_for_heredoc(list, line_no, col_start)
    126        when is_list(list) do
    127     # add 3 for """ (closing double quote)
    128     {line_no_end, col_end, _terminator} = convert_to_col_end(line_no, col_start, list)
    129 
    130     col_end = col_end + 3
    131 
    132     {line_no, col_start, line_no_end, col_end}
    133   end
    134 
    135   @doc false
    136   def position_tuple_for_quoted_string(list, line_no, col_start)
    137       when is_list(list) do
    138     # add 1 for " (closing double quote)
    139     {line_no_end, col_end, terminator} = convert_to_col_end(line_no, col_start, list)
    140 
    141     {line_no_end, col_end} =
    142       case terminator do
    143         :eol ->
    144           # move to next line
    145           {line_no_end + 1, 1}
    146 
    147         _ ->
    148           # add 1 for " (closing double quote)
    149           {line_no_end, col_end + 1}
    150       end
    151 
    152     {line_no, col_start, line_no_end, col_end}
    153   end
    154 
    155   #
    156 
    157   defp convert_to_col_end(line_no, col_start, list) when is_list(list) do
    158     Enum.reduce(list, {line_no, col_start, nil}, &reduce_to_col_end/2)
    159   end
    160 
    161   # Elixir < 1.9.0
    162   #
    163   # {{1, 25, 32}, [{:identifier, {1, 27, 31}, :name}]}
    164   defp convert_to_col_end(_, _, {{line_no, col_start, _}, list}) do
    165     {line_no_end, col_end, _terminator} = convert_to_col_end(line_no, col_start, list)
    166 
    167     # add 1 for } (closing parens of interpolation)
    168     col_end = col_end + 1
    169 
    170     {line_no_end, col_end, :interpolation}
    171   end
    172 
    173   # Elixir >= 1.9.0
    174   #
    175   # {{1, 25, nil}, {1, 31, nil}, [{:identifier, {1, 27, nil}, :name}]}
    176   defp convert_to_col_end(
    177          _,
    178          _,
    179          {{line_no, col_start, nil}, {_line_no2, _col_start2, nil}, list}
    180        ) do
    181     {line_no_end, col_end, _terminator} = convert_to_col_end(line_no, col_start, list)
    182 
    183     # add 1 for } (closing parens of interpolation)
    184     col_end = col_end + 1
    185 
    186     {line_no_end, col_end, :interpolation}
    187   end
    188 
    189   defp convert_to_col_end(_, _, {:eol, {line_no, col_start, _}}) do
    190     {line_no, col_start, :eol}
    191   end
    192 
    193   defp convert_to_col_end(_, _, {value, {line_no, col_start, _}}) do
    194     {line_no, to_col_end(col_start, value), nil}
    195   end
    196 
    197   defp convert_to_col_end(_, _, {:bin_string, {line_no, col_start, nil}, list})
    198        when is_list(list) do
    199     # add 2 for opening and closing "
    200     {line_no, col_end, terminator} =
    201       Enum.reduce(list, {line_no, col_start, nil}, &reduce_to_col_end/2)
    202 
    203     {line_no, col_end + 2, terminator}
    204   end
    205 
    206   defp convert_to_col_end(_, _, {:bin_string, {line_no, col_start, nil}, value}) do
    207     # add 2 for opening and closing "
    208     {line_no, to_col_end(col_start, value, 2), :bin_string}
    209   end
    210 
    211   defp convert_to_col_end(_, _, {:list_string, {line_no, col_start, nil}, value}) do
    212     # add 2 for opening and closing '
    213     {line_no, to_col_end(col_start, value, 2), :bin_string}
    214   end
    215 
    216   defp convert_to_col_end(_, _, {_, {line_no, col_start, nil}, list})
    217        when is_list(list) do
    218     Enum.reduce(list, {line_no, col_start, nil}, &reduce_to_col_end/2)
    219   end
    220 
    221   defp convert_to_col_end(_, _, {_, {line_no, col_start, nil}, value}) do
    222     {line_no, to_col_end(col_start, value), nil}
    223   end
    224 
    225   defp convert_to_col_end(_, _, {:aliases, {line_no, col_start, _}, list}) do
    226     value = Enum.map(list, &to_string/1)
    227 
    228     {line_no, to_col_end(col_start, value), nil}
    229   end
    230 
    231   defp convert_to_col_end(_, _, {_, {line_no, col_start, _}, value}) do
    232     {line_no, to_col_end(col_start, value), nil}
    233   end
    234 
    235   defp convert_to_col_end(_, _, {:sigil, {line_no, col_start, nil}, _, list, _, _})
    236        when is_list(list) do
    237     Enum.reduce(list, {line_no, col_start, nil}, &reduce_to_col_end/2)
    238   end
    239 
    240   # Elixir >= 1.11
    241   defp convert_to_col_end(
    242          _,
    243          _,
    244          {:sigil, {line_no, col_start, nil}, _, list, _list, _number, _binary}
    245        )
    246        when is_list(list) do
    247     Enum.reduce(list, {line_no, col_start, nil}, &reduce_to_col_end/2)
    248   end
    249 
    250   defp convert_to_col_end(_, _, {:sigil, {line_no, col_start, nil}, _, value, _, _}) do
    251     {line_no, to_col_end(col_start, value), nil}
    252   end
    253 
    254   defp convert_to_col_end(line_no, col_start, value) do
    255     {line_no, to_col_end(col_start, value), nil}
    256   end
    257 
    258   #
    259 
    260   defp reduce_to_col_end(value, {current_line_no, current_col_start, _}) do
    261     convert_to_col_end(current_line_no, current_col_start, value)
    262   end
    263 
    264   #
    265 
    266   def to_col_end(col_start, value, add \\ 0) do
    267     col_start + String.length(to_string(value)) + add
    268   end
    269 end