Skip to content

Commit

Permalink
Feature: Explicitly pick style fixups with :only & :exclude (#30)
Browse files Browse the repository at this point in the history
* Feature: Explicitly pick style fixups with :only & :exclude

* Update lib/quokka/config.ex

Co-authored-by: Emily Guthrie <[email protected]>
Signed-off-by: Ky Bishop <[email protected]>

* sort

* more docs

---------

Signed-off-by: Ky Bishop <[email protected]>
Co-authored-by: Emily Guthrie <[email protected]>
  • Loading branch information
kybishop and emkguts authored Feb 20, 2025
1 parent 3bae4cc commit f8c1615
Show file tree
Hide file tree
Showing 4 changed files with 120 additions and 78 deletions.
37 changes: 33 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,23 +57,52 @@ in `.formatter.exs` to fine tune your setup:
plugins: [Quokka],
quokka: [
inefficient_function_rewrites: true | false,
reorder_configs: true | false,
rewrite_deprecations: true | false,
files: %{
included: ["lib/", ...],
excluded: ["lib/example.ex", ...]
},
newline_fixes_only: true | false
only: [
# Changes to blocks of code
:blocks
# Comment directives such as # quokka:sort
| :comment_directives
# Sorting config files
| :configs
# Minimizes function heads
| :defs
# Converts deprecations
| :deprecations
# SPECIAL CASE: excludes all modules and only does newline fixups
| :line_length
# Fixes for imports, aliases, etc.
| :module_directives
# Various fixes for pipes
| :pipes
# Inefficient function rewrites, large numbers get underscores, etc.
# Basically anything that doesn't fit into the categories above
| :single_node
],
exclude: [
:blocks
| :comment_directives
| :configs
| :defs
| :deprecations
| :module_directives
| :pipes
| :single_node
]
]
]
```

| Option | Description | Default |
| -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- |
| `:files` | Quokka gets files from `.formatter.exs[:inputs]`. However, in some cases you may need to selectively exclude/include files you wish to still run in `mix format`, but have different behavior with Quokka. | `%{included: [], excluded: []}` (all files included, none excluded) |
| `:only` | Only include the given modules. The special `:line_length` option excludes all changes except line length fixups. | `[]` (all modules included) |
| `:exclude` | Exclude the given modules. This is just a convenience function that filters from the `:only` list. | `[]` (all modules included) |
| `:inefficient_function_rewrites` | Rewrite inefficient functions to more efficient form | `true` |
| `:reorder_configs` | Alphabetize `config` by key in `config/*.exs` files | `true` |
| `:rewrite_deprecations` | Rewrite deprecated functions to their new form | `true` |

## Credo inspired rewrites

Expand Down
3 changes: 2 additions & 1 deletion docs/mix_configs.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Mix Configs

> #### THIS CAN BREAK YOUR PROGRAM {: .warning}
>
> It's important to double check your configuration after running Quokka on it for the first time.
This can be enabled or disabled by setting the `:reorder_configs` option in your `.formatter.exs` file. See the [README](../README.md#configuration) for more information.
This can be enabled or disabled by setting the `exclude: :configs` option in your `.formatter.exs` file. See the [README](../README.md#configuration) for more information.

Mix Config files have their config stanzas sorted. Similar to the sorting of aliases, this delivers consistency to an otherwise arbitrary world, and can even help catch bugs like configuring the same key multiple times.

Expand Down
128 changes: 65 additions & 63 deletions lib/quokka/config.ex
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,17 @@ defmodule Quokka.Config do

@key __MODULE__

@styles [
ModuleDirectives,
Pipes,
SingleNode,
Defs,
Blocks,
Deprecations,
Configs,
CommentDirectives
]
# quokka:sort
@styles_by_atom %{
blocks: Blocks,
comment_directives: CommentDirectives,
configs: Configs,
defs: Defs,
deprecations: Deprecations,
module_directives: ModuleDirectives,
pipes: Pipes,
single_node: SingleNode
}

@stdlib ~w(
Access Agent Application Atom Base Behaviour Bitwise Code Date DateTime Dict Ecto Enum Exception
Expand All @@ -58,56 +59,46 @@ defmodule Quokka.Config do
ArgumentError -> set!(config)
end

def set!(config) do
def set!(config \\ []) do
credo_opts = extract_configs_from_credo()

lift_alias_excluded_namespaces =
(credo_opts[:lift_alias_excluded_namespaces] || []) |> Enum.map(&Atom.to_string/1)
Enum.map(credo_opts[:lift_alias_excluded_namespaces] || [], &Atom.to_string/1)

lift_alias_excluded_lastnames =
(credo_opts[:lift_alias_excluded_lastnames] || []) |> Enum.map(&Atom.to_string/1)

inefficient_function_rewrites =
if is_nil(config[:inefficient_function_rewrites]),
do: true,
else: config[:inefficient_function_rewrites]

newline_fixes_only = if is_nil(config[:newline_fixes_only]), do: false, else: config[:newline_fixes_only]

reorder_configs =
if is_nil(config[:reorder_configs]), do: true, else: config[:reorder_configs]

rewrite_deprecations =
if is_nil(config[:rewrite_deprecations]), do: true, else: config[:rewrite_deprecations]
Enum.map(credo_opts[:lift_alias_excluded_lastnames] || [], &Atom.to_string/1)

default_order = [:shortdoc, :moduledoc, :behaviour, :use, :import, :alias, :require]
strict_module_layout_order = credo_opts[:strict_module_layout_order] || default_order

:persistent_term.put(@key, %{
block_pipe_exclude: credo_opts[:block_pipe_exclude] || [],
block_pipe_flag: credo_opts[:block_pipe_flag] || false,
directories_excluded: Map.get(config[:files] || %{}, :excluded, []),
directories_included: Map.get(config[:files] || %{}, :included, []),
inefficient_function_rewrites: inefficient_function_rewrites,
large_numbers_gt: credo_opts[:large_numbers_gt] || :infinity,
lift_alias: credo_opts[:lift_alias] || false,
lift_alias_depth: credo_opts[:lift_alias_depth] || 0,
lift_alias_excluded_lastnames: MapSet.new(lift_alias_excluded_lastnames ++ @stdlib),
lift_alias_excluded_namespaces: MapSet.new(lift_alias_excluded_namespaces ++ @stdlib),
lift_alias_frequency: credo_opts[:lift_alias_frequency] || 0,
line_length: credo_opts[:line_length] || 98,
newline_fixes_only: newline_fixes_only,
pipe_chain_start_excluded_argument_types: credo_opts[:pipe_chain_start_excluded_argument_types] || [],
pipe_chain_start_excluded_functions: credo_opts[:pipe_chain_start_excluded_functions] || [],
pipe_chain_start_flag: credo_opts[:pipe_chain_start_flag] || false,
reorder_configs: reorder_configs,
rewrite_deprecations: rewrite_deprecations,
rewrite_multi_alias: credo_opts[:rewrite_multi_alias] || false,
single_pipe_flag: credo_opts[:single_pipe_flag] || false,
sort_order: credo_opts[:sort_order] || :alpha,
strict_module_layout_order: strict_module_layout_order ++ (default_order -- strict_module_layout_order),
zero_arity_parens: credo_opts[:zero_arity_parens] || false
})
:persistent_term.put(
@key,
# quokka:sort
%{
block_pipe_exclude: credo_opts[:block_pipe_exclude] || [],
block_pipe_flag: credo_opts[:block_pipe_flag] || false,
directories_excluded: Map.get(config[:files] || %{}, :excluded, []),
directories_included: Map.get(config[:files] || %{}, :included, []),
exclude_styles: config[:exclude] || [],
inefficient_function_rewrites: Keyword.get(config, :inefficient_function_rewrites, true),
large_numbers_gt: credo_opts[:large_numbers_gt] || :infinity,
lift_alias: credo_opts[:lift_alias] || false,
lift_alias_depth: credo_opts[:lift_alias_depth] || 0,
lift_alias_excluded_lastnames: MapSet.new(lift_alias_excluded_lastnames ++ @stdlib),
lift_alias_excluded_namespaces: MapSet.new(lift_alias_excluded_namespaces ++ @stdlib),
lift_alias_frequency: credo_opts[:lift_alias_frequency] || 0,
line_length: credo_opts[:line_length] || 98,
only_styles: config[:only] || [],
pipe_chain_start_excluded_argument_types: credo_opts[:pipe_chain_start_excluded_argument_types] || [],
pipe_chain_start_excluded_functions: credo_opts[:pipe_chain_start_excluded_functions] || [],
pipe_chain_start_flag: credo_opts[:pipe_chain_start_flag] || false,
rewrite_multi_alias: credo_opts[:rewrite_multi_alias] || false,
single_pipe_flag: credo_opts[:single_pipe_flag] || false,
sort_order: credo_opts[:sort_order] || :alpha,
strict_module_layout_order: strict_module_layout_order ++ (default_order -- strict_module_layout_order),
zero_arity_parens: credo_opts[:zero_arity_parens] || false
}
)
end

def set_for_test!(key, value) do
Expand All @@ -122,18 +113,29 @@ defmodule Quokka.Config do
end

def get_styles() do
if get(:newline_fixes_only) do
[Defs]
else
styles_to_remove =
for {module, flag_name} <- [
{Configs, :reorder_configs},
{Deprecations, :rewrite_deprecations}
],
do: if(get(flag_name), do: nil, else: module)

@styles -- styles_to_remove
end
styles_to_apply =
cond do
:line_length in only_styles() ->
[]

only_styles() == [] ->
Map.values(@styles_by_atom)

true ->
Enum.map(only_styles(), &@styles_by_atom[&1])
|> Enum.reject(&is_nil/1)
end

styles_to_exclude = Enum.map(exclude_styles(), &@styles_by_atom[&1])
Enum.filter(styles_to_apply, fn style -> !Enum.member?(styles_to_exclude, style) end)
end

def only_styles() do
get(:only_styles)
end

def exclude_styles() do
get(:exclude_styles)
end

def sort_order() do
Expand Down
30 changes: 20 additions & 10 deletions test/config_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -10,19 +10,29 @@ defmodule Quokka.ConfigTest do
assert :ok = set!([])
end

test "rewrite deprecations flag is respected" do
assert :ok = set!(rewrite_deprecations: false)
assert Deprecations not in Quokka.Config.get_styles()

assert :ok = set!(rewrite_deprecations: true)
assert Deprecations in Quokka.Config.get_styles()
test "respects the `:only` configuration" do
assert :ok = set!(only: [:deprecations])
assert [Deprecations] == Quokka.Config.get_styles()
end

test "reorder configs flag is respected" do
assert :ok = set!(reorder_configs: false)
assert Configs not in Quokka.Config.get_styles()
test "respects the `:exclude` configuration" do
assert :ok = set!(exclude: [:deprecations])

assert :ok = set!(reorder_configs: true)
# Check for one of the default configs
assert Configs in Quokka.Config.get_styles()

# Check that the excluded config is not present
assert Deprecations not in Quokka.Config.get_styles()
end

test "respects the `:only` and `:exclude` configuration" do
assert :ok = set!(only: [:configs, :deprecations], exclude: [:deprecations])

assert [Configs] == Quokka.Config.get_styles()
end

test "only applies line-length changes if :line_length is present in the `:only` configuration" do
assert :ok = set!(only: [:line_length])
assert [] == Quokka.Config.get_styles()
end
end

0 comments on commit f8c1615

Please sign in to comment.