Current section
27 Versions
Jump to
Current section
27 Versions
Compare versions
15
files changed
+955
additions
-461
deletions
| @@ -1 +1,144 @@ | |
| 1 | - # Changelog |
| \ No newline at end of file | |
| 1 | + # Changelog |
| 2 | + |
| 3 | + Quokka follows [Semantic Versioning](https://semver.org) and |
| 4 | + [Common Changelog: Guiding Principles](https://common-changelog.org/#12-guiding-principles) |
| 5 | + |
| 6 | + ## [1.1.0] - 2025-02-14 |
| 7 | + |
| 8 | + ### Improvements |
| 9 | + |
| 10 | + #### Line length formatting only |
| 11 | + |
| 12 | + In order to phase this into large codebases, Quokka now supports formatting only the line length, the idea being that it is easier to review a diff where one commit is just compressing vertical code and the following is the substantive rewrites -- aka the rewrites that change the AST. In order to use this feature, use `newline_fixes_only: true | false` in the config. |
| 13 | + |
| 14 | + ##### `# quokka:sort` Quokka's first comment directive |
| 15 | + |
| 16 | + Quokka will now keep a user-designated list or wordlist (`~w` sigil) sorted as part of formatting via the use of comments. Elements of the list are sorted by their string representation. It also works with maps, key-value pairs (sort by key), and `defstruct`, and even arbitrary ast nodes with a `do end` block. |
| 17 | + |
| 18 | + The intention is to remove comments to humans, like `# Please keep this list sorted!`, in favor of comments to robots: `# quokka:sort`. Personally speaking, Quokka is much better at alphabetical-order than I ever will be. |
| 19 | + |
| 20 | + To use the new directive, put it on the line before a list or wordlist. |
| 21 | + |
| 22 | + This example: |
| 23 | + |
| 24 | + ```elixir |
| 25 | + # quokka:sort |
| 26 | + [:c, :a, :b] |
| 27 | + |
| 28 | + # quokka:sort |
| 29 | + ~w(a list of words) |
| 30 | + |
| 31 | + # quokka:sort |
| 32 | + @country_codes ~w( |
| 33 | + en_US |
| 34 | + po_PO |
| 35 | + fr_CA |
| 36 | + ja_JP |
| 37 | + ) |
| 38 | + |
| 39 | + # quokka:sort |
| 40 | + a_var = |
| 41 | + [ |
| 42 | + Modules, |
| 43 | + In, |
| 44 | + A, |
| 45 | + List |
| 46 | + ] |
| 47 | + |
| 48 | + # quokka:sort |
| 49 | + my_macro "some arg" do |
| 50 | + another_macro :q |
| 51 | + another_macro :w |
| 52 | + another_macro :e |
| 53 | + another_macro :r |
| 54 | + another_macro :t |
| 55 | + another_macro :y |
| 56 | + end |
| 57 | + ``` |
| 58 | + |
| 59 | + Would yield: |
| 60 | + |
| 61 | + ```elixir |
| 62 | + # quokka:sort |
| 63 | + [:a, :b, :c] |
| 64 | + |
| 65 | + # quokka:sort |
| 66 | + ~w(a list of words) |
| 67 | + |
| 68 | + # quokka:sort |
| 69 | + @country_codes ~w( |
| 70 | + en_US |
| 71 | + fr_CA |
| 72 | + ja_JP |
| 73 | + po_PO |
| 74 | + ) |
| 75 | + |
| 76 | + # quokka:sort |
| 77 | + a_var = |
| 78 | + [ |
| 79 | + A, |
| 80 | + In, |
| 81 | + List, |
| 82 | + Modules |
| 83 | + ] |
| 84 | + |
| 85 | + # quokka:sort |
| 86 | + my_macro "some arg" do |
| 87 | + another_macro :e |
| 88 | + another_macro :q |
| 89 | + another_macro :r |
| 90 | + another_macro :t |
| 91 | + another_macro :w |
| 92 | + another_macro :y |
| 93 | + end |
| 94 | + ``` |
| 95 | + #### Other improvements |
| 96 | + - General improvements around conflict detection, lifting in more correct places and fewer incorrect places. |
| 97 | + - Use knowledge of existing aliases to shorten invocations. |
| 98 | + |
| 99 | + example: |
| 100 | + alias A.B.C |
| 101 | + |
| 102 | + A.B.C.foo() |
| 103 | + A.B.C.bar() |
| 104 | + A.B.C.baz() |
| 105 | + |
| 106 | + becomes: |
| 107 | + alias A.B.C |
| 108 | + |
| 109 | + C.foo() |
| 110 | + C.bar() |
| 111 | + C.baz() |
| 112 | + |
| 113 | + - Config Sorting: improve comment handling when only sorting a few nodes. |
| 114 | + - Pipes: pipe-ifies when first arg to a function is a pipe. reach out if this happens in unstylish places in your code. |
| 115 | + - Pipes: unpiping assignments will make the assignment one-line when possible |
| 116 | + - Deprecations: 1.18 deprecations |
| 117 | + - `List.zip` => `Enum.zip` |
| 118 | + - `first..last = range` => `first..last//_ = range` |
| 119 | + |
| 120 | + ### Fixes |
| 121 | + |
| 122 | + - Support the credo config of the format `checks: %{enabled: [...], disabled: [...]}`, whereas previously it expected `checks: [...]}` |
| 123 | + - Pipes: optimizations are less likely to move comments |
| 124 | + - Don't pipify when the call is itself in a pipe (aka don't touch a |> b(c |> d() |>e()) |> f()) |
| 125 | + |
| 126 | + ## [1.0.0] - 2025-02-10 |
| 127 | + |
| 128 | + Quokka is inspired by the wonderful [`elixir-styler`](https://github.com/adobe/elixir-styler) :heart: |
| 129 | + |
| 130 | + It maintains the same directive that consistent coding standards can help teams |
| 131 | + iterate quickly, but allows a few more affordances |
| 132 | + [via `.credo.exs` configuration](https://hexdocs.pm/credo/config_file.html). |
| 133 | + This allows users with an already fine-tuned `.credo.exs` config to enjoy |
| 134 | + the automatic rewrites and strong opinions of Quokka |
| 135 | + |
| 136 | + More details about specific Credo rewrites and their configurability can be |
| 137 | + found in [Quokka: Credo inspired rewrites](https://hexdocs.pm/quokka/readme.html#credo-inspired-rewrites). |
| 138 | + |
| 139 | + Adoption of opinionated code changes can be hard in larger code bases, so |
| 140 | + Quokka allows a few configuration options in `.formatter.exs` to help |
| 141 | + isolate big sets of potentially controversial or code breaking changes that |
| 142 | + may need time for adoption. However, these may be removed in a future release. |
| 143 | + See [Quokka: Configuration](https://hexdocs.pm/quokka/readme.html#configuration) |
| 144 | + for more details. |
| @@ -4,15 +4,16 @@ | |
| 4 4 | |
| 5 5 | # Quokka |
| 6 6 | |
| 7 | - <img src="docs/assets/quokka.png" alt="A happy quokka with style" width="300"/> |
| 7 | + <img src="docs/assets/quokka.png" alt="A happy quokka with style" width="300"/> |
| 8 8 | |
| 9 | - Quokka is an Elixir formatter plugin that's combination of `mix format` and `mix credo`, except instead of telling you what's wrong, it just rewrites the code for you. Quokka is a fork of [Styler](https://github.com/adobe/styler) that checks the Credo config to determine which rules to rewrite. Many common, non-controversial Credo style rules are rewritten automatically, while the controversial Credo style rules are rewritten based on your Credo configuration so you can customize your style. |
| 9 | + Quokka is an Elixir formatter plugin that's combination of `mix format` and `mix credo`, except instead of telling you what's wrong, it just rewrites the code for you. Quokka is a fork of [Styler](https://github.com/adobe/elixir-styler) that checks the Credo config to determine which rules to rewrite. Many common, non-controversial Credo style rules are rewritten automatically, while the controversial Credo style rules are rewritten based on your Credo configuration so you can customize your style. |
| 10 10 | |
| 11 11 | > #### WARNING {: .warning} |
| 12 | + > |
| 12 13 | > Quokka can change the behavior of your program! |
| 13 | - > |
| 14 | + > |
| 14 15 | > In some cases, this can introduce bugs. It goes without saying, but look over your changes before committing to main :) |
| 15 | - > |
| 16 | + > |
| 16 17 | > We recommend making changes in small chunks until all of the more dangerous |
| 17 18 | > changes has been safely committed to the codebase |
| 18 19 | |
| @@ -38,6 +39,11 @@ Then add `Quokka` as a plugin to your `.formatter.exs` file | |
| 38 39 | |
| 39 40 | And that's it! Now when you run `mix format` you'll also get the benefits of Quokka's Stylish Stylings. |
| 40 41 | |
| 42 | + ### First Run |
| 43 | + |
| 44 | + You may want to initially run Quokka in "newline fixes only" mode. This will only fix spacing issues, making future PRs _much_ smaller and easier to digest. |
| 45 | + See the example in the configuration section if you wish to do this. |
| 46 | + |
| 41 47 | **Speed**: Expect the first run to take some time as `Quokka` rewrites violations of styles and bottlenecks on disk I/O. Subsequent formats will take noticeably less time. |
| 42 48 | |
| 43 49 | ### Configuration |
| @@ -56,16 +62,18 @@ in `.formatter.exs` to fine tune your setup: | |
| 56 62 | files: %{ |
| 57 63 | included: ["lib/", ...], |
| 58 64 | excluded: ["lib/example.ex", ...] |
| 59 | - } |
| 65 | + }, |
| 66 | + newline_fixes_only: true | false |
| 60 67 | ] |
| 61 68 | ] |
| 62 69 | ``` |
| 63 | - | Option | Description | Default | |
| 64 | - | --- | --- | --- | |
| 65 | - | `: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) | |
| 66 | - | `:inefficient_function_rewrites` | Rewrite inefficient functions to more efficient form | `true` | |
| 67 | - | `:reorder_configs` | Alphabetize `config` by key in `config/*.exs` files | `true` | |
| 68 | - | `:rewrite_deprecations` | Rewrite deprecated functions to their new form | `true` | |
| 70 | + |
| 71 | + | Option | Description | Default | |
| 72 | + | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------- | |
| 73 | + | `: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) | |
| 74 | + | `:inefficient_function_rewrites` | Rewrite inefficient functions to more efficient form | `true` | |
| 75 | + | `:reorder_configs` | Alphabetize `config` by key in `config/*.exs` files | `true` | |
| 76 | + | `:rewrite_deprecations` | Rewrite deprecated functions to their new form | `true` | |
| 69 77 | |
| 70 78 | ## Credo inspired rewrites |
| 71 79 | |
| @@ -77,7 +85,7 @@ some additional useful details such as links to detailed documentation and if | |
| 77 85 | the check can be configured further for fine tuning. |
| 78 86 | |
| 79 87 | > #### `:controversial` Credo checks {: .tip} |
| 80 | - > |
| 88 | + > |
| 81 89 | > Quokka allows all `:controversial` Credo checks to be configurable. In many cases, |
| 82 90 | > a Credo check can also be disabled to prevent rewriting. |
| 83 91 | |
| @@ -85,50 +93,50 @@ the check can be configured further for fine tuning. | |
| 85 93 | |
| 86 94 | ### Credo.Check.Consistency |
| 87 95 | |
| 88 | - | Credo Check | Rewrite Description | Documentation | Configurable | |
| 89 | - |-------------|-------------------|---------------|--------------| |
| 90 | - | [`.MultiAliasImportRequireUse`](https://hexdocs.pm/credo/Credo.Check.Consistency.MultiAliasImportRequireUse.html) | Expands multi-alias/import statements | [Directive Expansion](docs/module_directives.md#directive-expansion) | | |
| 91 | - | [`.ParameterPatternMatching`](https://hexdocs.pm/credo/Credo.Check.Consistency.ParameterPatternMatching.html) | Enforces consistent parameter pattern matching | [Parameter Pattern Matching](docs/styles.md#parameter-pattern-matching-consistency) | | |
| 96 | + | Credo Check | Rewrite Description | Documentation | Configurable | |
| 97 | + | ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | ----------------------------------------------------------------------------------- | ------------ | |
| 98 | + | [`.MultiAliasImportRequireUse`](https://hexdocs.pm/credo/Credo.Check.Consistency.MultiAliasImportRequireUse.html) | Expands multi-alias/import statements | [Directive Expansion](docs/module_directives.md#directive-expansion) | | |
| 99 | + | [`.ParameterPatternMatching`](https://hexdocs.pm/credo/Credo.Check.Consistency.ParameterPatternMatching.html) | Enforces consistent parameter pattern matching | [Parameter Pattern Matching](docs/styles.md#parameter-pattern-matching-consistency) | | |
| 92 100 | |
| 93 101 | ### Credo.Check.Design |
| 94 102 | |
| 95 | - | Credo Check | Rewrite Description | Documentation | Configurable | |
| 96 | - |-------------|-------------------|---------------|--------------| |
| 97 | - | [`.AliasUsage`](https://hexdocs.pm/credo/Credo.Check.Design.AliasUsage.html) | Extracts repeated aliases | [Alias Lifting](docs/module_directives.md#alias-lifting) | âś“ | |
| 103 | + | Credo Check | Rewrite Description | Documentation | Configurable | |
| 104 | + | ---------------------------------------------------------------------------- | ------------------------- | -------------------------------------------------------- | ------------ | |
| 105 | + | [`.AliasUsage`](https://hexdocs.pm/credo/Credo.Check.Design.AliasUsage.html) | Extracts repeated aliases | [Alias Lifting](docs/module_directives.md#alias-lifting) | âś“ | |
| 98 106 | |
| 99 107 | ### Credo.Check.Readability |
| 100 108 | |
| 101 | - | Credo Check | Rewrite Description | Documentation | Configurable | |
| 102 | - |-------------|-------------------|---------------|--------------| |
| 103 | - | [`.AliasOrder`](https://hexdocs.pm/credo/Credo.Check.Readability.AliasOrder.html) | Alphabetizes module directives | [Module Directives](docs/module_directives.md#directive-organization) | âś“ | |
| 104 | - | [`.BlockPipe`](https://hexdocs.pm/credo/Credo.Check.Readability.BlockPipe.html) | (En\|dis)ables piping into blocks | [Pipe Chains](docs/pipes.md#pipe-start) | âś“ | |
| 105 | - | [`.LargeNumbers`](https://hexdocs.pm/credo/Credo.Check.Readability.LargeNumbers.html) | Formats large numbers with underscores | [Number Formatting](docs/styles.md#large-base-10-numbers) | âś“ | |
| 106 | - | [`.MaxLineLength`](https://hexdocs.pm/credo/Credo.Check.Readability.MaxLineLength.html) | Enforces maximum line length | [Line Length](docs/styles.md#line-length) | âś“ | |
| 107 | - | [`.MultiAlias`](https://hexdocs.pm/credo/Credo.Check.Readability.MultiAlias.html) | Expands multi-alias statements | [Module Directives](docs/module_directives.md#directive-expansion) | âś“ | |
| 108 | - | [`.OneArityFunctionInPipe`](https://hexdocs.pm/credo/Credo.Check.Readability.OneArityFunctionInPipe.html) | Optimizes pipe chains with single arity functions | [Pipe Chains](docs/pipes.md#add-parenthesis-to-function-calls-in-pipes) | | |
| 109 | - | [`.ParenthesesOnZeroArityDefs`](https://hexdocs.pm/credo/Credo.Check.Readability.ParenthesesOnZeroArityDefs.html) | Enforces consistent function call parentheses | [Function Calls](docs/styles.md#add-parenthesis-to-0-arity-functions-and-macro-definitions) | âś“ | |
| 110 | - | [`.PipeIntoAnonymousFunctions`](https://hexdocs.pm/credo/Credo.Check.Readability.PipeIntoAnonymousFunctions.html) | Optimizes pipes with anonymous functions | [Pipe Chains](docs/pipes.md#add-then-2-when-defining-and-calling-anonymous-functions-in-pipes) | | |
| 111 | - | [`.PreferImplicitTry`](https://hexdocs.pm/credo/Credo.Check.Readability.PreferImplicitTry.html) | Simplifies try expressions | [Control Flow Macros](docs/styles.md#implicit-try) | | |
| 112 | - | [`.SinglePipe`](https://hexdocs.pm/credo/Credo.Check.Readability.SinglePipe.html) | Optimizes pipe chains | [Pipe Chains](docs/pipes.md#unpiping-single-pipes) | âś“ | |
| 113 | - | [`.StringSigils`](https://hexdocs.pm/credo/Credo.Check.Readability.StringSigils.html) | Replaces strings with sigils | [Strings to Sigils](docs/styles.md#strings-to-sigils) | | |
| 114 | - | [`.StrictModuleLayout`](https://hexdocs.pm/credo/Credo.Check.Readability.StrictModuleLayout.html) | Enforces strict module layout | [Module Directives](docs/module_directives.md#directive-organization) | âś“ | |
| 115 | - | [`.UnnecessaryAliasExpansion`](https://hexdocs.pm/credo/Credo.Check.Readability.UnnecessaryAliasExpansion.html) | Removes unnecessary alias expansions | [Module Directives](docs/module_directives.md#directive-expansion) | | |
| 116 | - | [`.WithSingleClause`](https://hexdocs.pm/credo/Credo.Check.Readability.WithSingleClause.html) | Simplifies with statements | [Control Flow Macros](docs/control_flow_macros.md#with) | | |
| 109 | + | Credo Check | Rewrite Description | Documentation | Configurable | |
| 110 | + | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------------ | |
| 111 | + | [`.AliasOrder`](https://hexdocs.pm/credo/Credo.Check.Readability.AliasOrder.html) | Alphabetizes module directives | [Module Directives](docs/module_directives.md#directive-organization) | âś“ | |
| 112 | + | [`.BlockPipe`](https://hexdocs.pm/credo/Credo.Check.Readability.BlockPipe.html) | (En\|dis)ables piping into blocks | [Pipe Chains](docs/pipes.md#pipe-start) | âś“ | |
| 113 | + | [`.LargeNumbers`](https://hexdocs.pm/credo/Credo.Check.Readability.LargeNumbers.html) | Formats large numbers with underscores | [Number Formatting](docs/styles.md#large-base-10-numbers) | âś“ | |
| 114 | + | [`.MaxLineLength`](https://hexdocs.pm/credo/Credo.Check.Readability.MaxLineLength.html) | Enforces maximum line length | [Line Length](docs/styles.md#line-length) | âś“ | |
| 115 | + | [`.MultiAlias`](https://hexdocs.pm/credo/Credo.Check.Readability.MultiAlias.html) | Expands multi-alias statements | [Module Directives](docs/module_directives.md#directive-expansion) | âś“ | |
| 116 | + | [`.OneArityFunctionInPipe`](https://hexdocs.pm/credo/Credo.Check.Readability.OneArityFunctionInPipe.html) | Optimizes pipe chains with single arity functions | [Pipe Chains](docs/pipes.md#add-parenthesis-to-function-calls-in-pipes) | | |
| 117 | + | [`.ParenthesesOnZeroArityDefs`](https://hexdocs.pm/credo/Credo.Check.Readability.ParenthesesOnZeroArityDefs.html) | Enforces consistent function call parentheses | [Function Calls](docs/styles.md#add-parenthesis-to-0-arity-functions-and-macro-definitions) | âś“ | |
| 118 | + | [`.PipeIntoAnonymousFunctions`](https://hexdocs.pm/credo/Credo.Check.Readability.PipeIntoAnonymousFunctions.html) | Optimizes pipes with anonymous functions | [Pipe Chains](docs/pipes.md#add-then-2-when-defining-and-calling-anonymous-functions-in-pipes) | | |
| 119 | + | [`.PreferImplicitTry`](https://hexdocs.pm/credo/Credo.Check.Readability.PreferImplicitTry.html) | Simplifies try expressions | [Control Flow Macros](docs/styles.md#implicit-try) | | |
| 120 | + | [`.SinglePipe`](https://hexdocs.pm/credo/Credo.Check.Readability.SinglePipe.html) | Optimizes pipe chains | [Pipe Chains](docs/pipes.md#unpiping-single-pipes) | âś“ | |
| 121 | + | [`.StringSigils`](https://hexdocs.pm/credo/Credo.Check.Readability.StringSigils.html) | Replaces strings with sigils | [Strings to Sigils](docs/styles.md#strings-to-sigils) | | |
| 122 | + | [`.StrictModuleLayout`](https://hexdocs.pm/credo/Credo.Check.Readability.StrictModuleLayout.html) | Enforces strict module layout | [Module Directives](docs/module_directives.md#directive-organization) | âś“ | |
| 123 | + | [`.UnnecessaryAliasExpansion`](https://hexdocs.pm/credo/Credo.Check.Readability.UnnecessaryAliasExpansion.html) | Removes unnecessary alias expansions | [Module Directives](docs/module_directives.md#directive-expansion) | | |
| 124 | + | [`.WithSingleClause`](https://hexdocs.pm/credo/Credo.Check.Readability.WithSingleClause.html) | Simplifies with statements | [Control Flow Macros](docs/control_flow_macros.md#with) | | |
| 117 125 | |
| 118 126 | ### Credo.Check.Refactor |
| 119 127 | |
| 120 | - | Credo Check | Rewrite Description | Documentation | Configurable | |
| 121 | - |-------------|-------------------|---------------|--------------| |
| 122 | - | [`.CondStatements`](https://hexdocs.pm/credo/Credo.Check.Refactor.CondStatements.html) | Simplifies boolean expressions | [Control Flow Macros](docs/control_flow_macros.md#cond) | | |
| 123 | - | [`.FilterCount`](https://hexdocs.pm/credo/Credo.Check.Refactor.FilterCount.html) | Optimizes filter + count operations | [Styles](docs/styles.md#filter-count) | | |
| 124 | - | [`.MapInto`](https://hexdocs.pm/credo/Credo.Check.Refactor.MapInto.html) | Optimizes map + into operations | [Styles](docs/styles.md#map-into) | | |
| 125 | - | [`.MapJoin`](https://hexdocs.pm/credo/Credo.Check.Refactor.MapJoin.html) | Optimizes map + join operations | [Styles](docs/styles.md#map-join) | | |
| 126 | - | [`.NegatedConditionsInUnless`](https://hexdocs.pm/credo/Credo.Check.Refactor.NegatedConditionsInUnless.html) | Simplifies negated conditions in unless | [Control Flow Macros](docs/control_flow_macros.md#if-and-unless) | | |
| 127 | - | [`.NegatedConditionsWithElse`](https://hexdocs.pm/credo/Credo.Check.Refactor.NegatedConditionsWithElse.html) | Simplifies negated conditions with else | [Control Flow Macros](docs/control_flow_macros.md#negation-inversion) | | |
| 128 | - | [`.PipeChainStart`](https://hexdocs.pm/credo/Credo.Check.Refactor.PipeChainStart.html) | Optimizes pipe chain start | [Pipe Chains](docs/pipes.md#pipe-start) | | |
| 129 | - | [`.RedundantWithClauseResult`](https://hexdocs.pm/credo/Credo.Check.Refactor.RedundantWithClauseResult.html) | Removes redundant with clause results | [Control Flow Macros](docs/control_flow_macros.md#with) | | |
| 130 | - | [`.UnlessWithElse`](https://hexdocs.pm/credo/Credo.Check.Refactor.UnlessWithElse.html) | Simplifies unless with else | [Control Flow Macros](docs/control_flow_macros.md#if-and-unless) | | |
| 131 | - | [`.WithClauses`](https://hexdocs.pm/credo/Credo.Check.Refactor.WithClauses.html) | Optimizes with clauses | [Control Flow Macros](docs/control_flow_macros.md#with) | | |
| 128 | + | Credo Check | Rewrite Description | Documentation | Configurable | |
| 129 | + | ------------------------------------------------------------------------------------------------------------ | --------------------------------------- | --------------------------------------------------------------------- | ------------ | |
| 130 | + | [`.CondStatements`](https://hexdocs.pm/credo/Credo.Check.Refactor.CondStatements.html) | Simplifies boolean expressions | [Control Flow Macros](docs/control_flow_macros.md#cond) | | |
| 131 | + | [`.FilterCount`](https://hexdocs.pm/credo/Credo.Check.Refactor.FilterCount.html) | Optimizes filter + count operations | [Styles](docs/styles.md#filter-count) | | |
| 132 | + | [`.MapInto`](https://hexdocs.pm/credo/Credo.Check.Refactor.MapInto.html) | Optimizes map + into operations | [Styles](docs/styles.md#map-into) | | |
| 133 | + | [`.MapJoin`](https://hexdocs.pm/credo/Credo.Check.Refactor.MapJoin.html) | Optimizes map + join operations | [Styles](docs/styles.md#map-join) | | |
| 134 | + | [`.NegatedConditionsInUnless`](https://hexdocs.pm/credo/Credo.Check.Refactor.NegatedConditionsInUnless.html) | Simplifies negated conditions in unless | [Control Flow Macros](docs/control_flow_macros.md#if-and-unless) | | |
| 135 | + | [`.NegatedConditionsWithElse`](https://hexdocs.pm/credo/Credo.Check.Refactor.NegatedConditionsWithElse.html) | Simplifies negated conditions with else | [Control Flow Macros](docs/control_flow_macros.md#negation-inversion) | | |
| 136 | + | [`.PipeChainStart`](https://hexdocs.pm/credo/Credo.Check.Refactor.PipeChainStart.html) | Optimizes pipe chain start | [Pipe Chains](docs/pipes.md#pipe-start) | | |
| 137 | + | [`.RedundantWithClauseResult`](https://hexdocs.pm/credo/Credo.Check.Refactor.RedundantWithClauseResult.html) | Removes redundant with clause results | [Control Flow Macros](docs/control_flow_macros.md#with) | | |
| 138 | + | [`.UnlessWithElse`](https://hexdocs.pm/credo/Credo.Check.Refactor.UnlessWithElse.html) | Simplifies unless with else | [Control Flow Macros](docs/control_flow_macros.md#if-and-unless) | | |
| 139 | + | [`.WithClauses`](https://hexdocs.pm/credo/Credo.Check.Refactor.WithClauses.html) | Optimizes with clauses | [Control Flow Macros](docs/control_flow_macros.md#with) | | |
| 132 140 | |
| 133 141 | <!-- tabs-close --> |
| @@ -1,6 +1,6 @@ | |
| 1 1 | {<<"links">>,[{<<"GitHub">>,<<"https://github.com/smartrent/quokka">>}]}. |
| 2 2 | {<<"name">>,<<"quokka">>}. |
| 3 | - {<<"version">>,<<"1.0.0">>}. |
| 3 | + {<<"version">>,<<"1.1.0">>}. |
| 4 4 | {<<"description">>, |
| 5 5 | <<"A Credo-configured code-style enforcer that will just fix it for you instead of complaining">>}. |
| 6 6 | {<<"elixir">>,<<"~> 1.15">>}. |
| @@ -16,8 +16,9 @@ | |
| 16 16 | [<<"lib">>,<<"lib/zipper.ex">>,<<"lib/quokka">>,<<"lib/quokka/config.ex">>, |
| 17 17 | <<"lib/style">>,<<"lib/style/single_node.ex">>,<<"lib/style/defs.ex">>, |
| 18 18 | <<"lib/style/configs.ex">>,<<"lib/style/blocks.ex">>, |
| 19 | - <<"lib/style/module_directives.ex">>,<<"lib/style/deprecations.ex">>, |
| 20 | - <<"lib/style/pipes.ex">>,<<"lib/style.ex">>,<<"lib/alias_env.ex">>, |
| 21 | - <<"lib/style_error.ex">>,<<"lib/styler.ex">>,<<".formatter.exs">>, |
| 22 | - <<"mix.exs">>,<<"README.md">>,<<"LICENSE">>,<<"CHANGELOG.md">>]}. |
| 19 | + <<"lib/style/module_directives.ex">>,<<"lib/style/comment_directives.ex">>, |
| 20 | + <<"lib/style/deprecations.ex">>,<<"lib/style/pipes.ex">>,<<"lib/style.ex">>, |
| 21 | + <<"lib/alias_env.ex">>,<<"lib/style_error.ex">>,<<"lib/styler.ex">>, |
| 22 | + <<".formatter.exs">>,<<"mix.exs">>,<<"README.md">>,<<"LICENSE">>, |
| 23 | + <<"CHANGELOG.md">>]}. |
| 23 24 | {<<"build_tools">>,[<<"mix">>]}. |
| @@ -22,18 +22,26 @@ defmodule Quokka.Config do | |
| 22 22 | alias Credo.Check.Readability.SinglePipe |
| 23 23 | alias Credo.Check.Readability.StrictModuleLayout |
| 24 24 | alias Credo.Check.Refactor.PipeChainStart |
| 25 | + alias Quokka.Style.Blocks |
| 26 | + alias Quokka.Style.CommentDirectives |
| 25 27 | alias Quokka.Style.Configs |
| 28 | + alias Quokka.Style.Defs |
| 29 | + alias Quokka.Style.Deprecations |
| 30 | + alias Quokka.Style.ModuleDirectives |
| 31 | + alias Quokka.Style.Pipes |
| 32 | + alias Quokka.Style.SingleNode |
| 26 33 | |
| 27 34 | @key __MODULE__ |
| 28 35 | |
| 29 36 | @styles [ |
| 30 | - Quokka.Style.ModuleDirectives, |
| 31 | - Quokka.Style.Pipes, |
| 32 | - Quokka.Style.SingleNode, |
| 33 | - Quokka.Style.Defs, |
| 34 | - Quokka.Style.Blocks, |
| 35 | - Quokka.Style.Deprecations, |
| 36 | - Configs |
| 37 | + ModuleDirectives, |
| 38 | + Pipes, |
| 39 | + SingleNode, |
| 40 | + Defs, |
| 41 | + Blocks, |
| 42 | + Deprecations, |
| 43 | + Configs, |
| 44 | + CommentDirectives |
| 37 45 | ] |
| 38 46 | |
| 39 47 | @stdlib ~w( |
| @@ -53,15 +61,22 @@ defmodule Quokka.Config do | |
| 53 61 | def set!(config) do |
| 54 62 | credo_opts = extract_configs_from_credo() |
| 55 63 | |
| 56 | - lift_alias_excluded_namespaces = (credo_opts[:lift_alias_excluded_namespaces] || []) |> Enum.map(&Atom.to_string/1) |
| 57 | - lift_alias_excluded_lastnames = (credo_opts[:lift_alias_excluded_lastnames] || []) |> Enum.map(&Atom.to_string/1) |
| 64 | + lift_alias_excluded_namespaces = |
| 65 | + (credo_opts[:lift_alias_excluded_namespaces] || []) |> Enum.map(&Atom.to_string/1) |
| 66 | + |
| 67 | + lift_alias_excluded_lastnames = |
| 68 | + (credo_opts[:lift_alias_excluded_lastnames] || []) |> Enum.map(&Atom.to_string/1) |
| 69 | + |
| 70 | + inefficient_function_rewrites = |
| 71 | + if is_nil(config[:inefficient_function_rewrites]), |
| 72 | + do: true, |
| 73 | + else: config[:inefficient_function_rewrites] |
| 74 | + |
| 75 | + newline_fixes_only = if is_nil(config[:newline_fixes_only]), do: false, else: config[:newline_fixes_only] |
| 58 76 | |
| 59 77 | reorder_configs = |
| 60 78 | if is_nil(config[:reorder_configs]), do: true, else: config[:reorder_configs] |
| 61 79 | |
| 62 | - inefficient_function_rewrites = |
| 63 | - if is_nil(config[:inefficient_function_rewrites]), do: true, else: config[:inefficient_function_rewrites] |
| 64 | - |
| 65 80 | rewrite_deprecations = |
| 66 81 | if is_nil(config[:rewrite_deprecations]), do: true, else: config[:rewrite_deprecations] |
| 67 82 | |
| @@ -69,28 +84,29 @@ defmodule Quokka.Config do | |
| 69 84 | strict_module_layout_order = credo_opts[:strict_module_layout_order] || default_order |
| 70 85 | |
| 71 86 | :persistent_term.put(@key, %{ |
| 72 | - block_pipe_flag: credo_opts[:block_pipe_flag] || false, |
| 73 87 | block_pipe_exclude: credo_opts[:block_pipe_exclude] || [], |
| 74 | - directories_included: Map.get(config[:files] || %{}, :included, []), |
| 88 | + block_pipe_flag: credo_opts[:block_pipe_flag] || false, |
| 75 89 | directories_excluded: Map.get(config[:files] || %{}, :excluded, []), |
| 90 | + directories_included: Map.get(config[:files] || %{}, :included, []), |
| 76 91 | inefficient_function_rewrites: inefficient_function_rewrites, |
| 77 92 | large_numbers_gt: credo_opts[:large_numbers_gt] || :infinity, |
| 78 | - line_length: credo_opts[:line_length] || 98, |
| 79 | - pipe_chain_start_flag: credo_opts[:pipe_chain_start_flag] || false, |
| 80 | - pipe_chain_start_excluded_functions: credo_opts[:pipe_chain_start_excluded_functions] || [], |
| 81 | - pipe_chain_start_excluded_argument_types: credo_opts[:pipe_chain_start_excluded_argument_types] || [], |
| 82 | - reorder_configs: reorder_configs, |
| 83 | - rewrite_deprecations: rewrite_deprecations, |
| 84 93 | lift_alias: credo_opts[:lift_alias] || false, |
| 85 94 | lift_alias_depth: credo_opts[:lift_alias_depth] || 0, |
| 86 | - lift_alias_excluded_namespaces: MapSet.new(lift_alias_excluded_namespaces ++ @stdlib), |
| 87 95 | lift_alias_excluded_lastnames: MapSet.new(lift_alias_excluded_lastnames ++ @stdlib), |
| 96 | + lift_alias_excluded_namespaces: MapSet.new(lift_alias_excluded_namespaces ++ @stdlib), |
| 88 97 | lift_alias_frequency: credo_opts[:lift_alias_frequency] || 0, |
| 98 | + line_length: credo_opts[:line_length] || 98, |
| 99 | + newline_fixes_only: newline_fixes_only, |
| 100 | + pipe_chain_start_excluded_argument_types: credo_opts[:pipe_chain_start_excluded_argument_types] || [], |
| 101 | + pipe_chain_start_excluded_functions: credo_opts[:pipe_chain_start_excluded_functions] || [], |
| 102 | + pipe_chain_start_flag: credo_opts[:pipe_chain_start_flag] || false, |
| 103 | + reorder_configs: reorder_configs, |
| 104 | + rewrite_deprecations: rewrite_deprecations, |
| 89 105 | rewrite_multi_alias: credo_opts[:rewrite_multi_alias] || false, |
| 90 106 | single_pipe_flag: credo_opts[:single_pipe_flag] || false, |
| 91 107 | sort_order: credo_opts[:sort_order] || :alpha, |
| 92 108 | strict_module_layout_order: strict_module_layout_order ++ (default_order -- strict_module_layout_order), |
| 93 | - zero_arity_parens: credo_opts[:zero_arity_parens] || false, |
| 109 | + zero_arity_parens: credo_opts[:zero_arity_parens] || false |
| 94 110 | }) |
| 95 111 | end |
| 96 112 | |
| @@ -106,11 +122,18 @@ defmodule Quokka.Config do | |
| 106 122 | end |
| 107 123 | |
| 108 124 | def get_styles() do |
| 109 | - styles_to_remove = |
| 110 | - for {module, flag_name} <- [{Configs, :reorder_configs}, {Quokka.Style.Deprecations, :rewrite_deprecations}], |
| 111 | - do: (if get(flag_name), do: nil, else: module) |
| 125 | + if get(:newline_fixes_only) do |
| 126 | + [Defs] |
| 127 | + else |
| 128 | + styles_to_remove = |
| 129 | + for {module, flag_name} <- [ |
| 130 | + {Configs, :reorder_configs}, |
| 131 | + {Deprecations, :rewrite_deprecations} |
| 132 | + ], |
| 133 | + do: if(get(flag_name), do: nil, else: module) |
| 112 134 | |
| 113 | - @styles -- styles_to_remove |
| 135 | + @styles -- styles_to_remove |
| 136 | + end |
| 114 137 | end |
| 115 138 | |
| 116 139 | def sort_order() do |
| @@ -205,7 +228,11 @@ defmodule Quokka.Config do | |
| 205 228 | end |
| 206 229 | |
| 207 230 | defp extract_configs_from_credo() do |
| 208 | - Enum.reduce(read_credo_config().checks, %{}, fn |
| 231 | + case read_credo_config().checks do |
| 232 | + checks when is_list(checks) -> checks |
| 233 | + checks -> Map.get(checks, :enabled, []) |
| 234 | + end |
| 235 | + |> Enum.reduce(%{}, fn |
| 209 236 | {AliasOrder, opts}, acc when is_list(opts) -> |
| 210 237 | Map.put(acc, :sort_order, opts[:sort_method]) |
| @@ -60,7 +60,7 @@ defmodule Quokka.Style do | |
| 60 60 | @doc "Traverses an ast node, updating all nodes' meta with `meta_fun`" |
| 61 61 | def update_all_meta(node, meta_fun), do: Macro.prewalk(node, &Macro.update_meta(&1, meta_fun)) |
| 62 62 | |
| 63 | - # useful for comparing AST without meta (line numbers, etc) interfering |
| 63 | + @doc "prewalks ast and sets all meta to `nil`. useful for comparing AST without meta (line numbers, etc) interfering" |
| 64 64 | def without_meta(ast), do: update_all_meta(ast, fn _ -> nil end) |
| 65 65 | |
| 66 66 | @doc """ |
| @@ -84,6 +84,9 @@ defmodule Quokka.Style do | |
| 84 84 | end |
| 85 85 | end |
| 86 86 | |
| 87 | + def do_block?([{{:__block__, _, [:do]}, _body} | _]), do: true |
| 88 | + def do_block?(_), do: false |
| 89 | + |
| 87 90 | @doc """ |
| 88 91 | Returns a zipper focused on the nearest node where additional nodes can be inserted (a "block"). |
| 89 92 | |
| @@ -109,7 +112,9 @@ defmodule Quokka.Style do | |
| 109 112 | # give it a block parent, then step back to the child - we can insert next to it now that it's in a block |
| 110 113 | defp wrap_in_block(zipper) do |
| 111 114 | zipper |
| 112 | - |> Zipper.update(fn {_, meta, _} = node -> {:__block__, Keyword.take(meta, [:line]), [node]} end) |
| 115 | + |> Zipper.update(fn {_, meta, _} = node -> |
| 116 | + {:__block__, Keyword.take(meta, [:line]), [node]} |
| 117 | + end) |
| 113 118 | |> Zipper.down() |
| 114 119 | end |
| 115 120 | |
| @@ -145,7 +150,7 @@ defmodule Quokka.Style do | |
| 145 150 | comments |
| 146 151 | |> Enum.map(fn comment -> |
| 147 152 | if delta = Enum.find_value(shifts, fn {range, delta} -> comment.line in range && delta end) do |
| 148 | - %{comment | line: comment.line + delta} |
| 153 | + %{comment | line: max(comment.line + delta, 1)} |
| 149 154 | else |
| 150 155 | comment |
| 151 156 | end |
| @@ -161,84 +166,134 @@ defmodule Quokka.Style do | |
| 161 166 | def reset_newlines(nodes), do: reset_newlines(nodes, []) |
| 162 167 | |
| 163 168 | def reset_newlines([node], acc), do: Enum.reverse([set_newlines(node, 2) | acc]) |
| 169 | + |
| 164 170 | def reset_newlines([node | nodes], acc), do: reset_newlines(nodes, [set_newlines(node, 1) | acc]) |
| 165 171 | |
| 166 172 | defp set_newlines({directive, meta, children}, newline) do |
| 167 | - updated_meta = Keyword.update(meta, :end_of_expression, [newlines: newline], &Keyword.put(&1, :newlines, newline)) |
| 173 | + updated_meta = |
| 174 | + Keyword.update( |
| 175 | + meta, |
| 176 | + :end_of_expression, |
| 177 | + [newlines: newline], |
| 178 | + &Keyword.put(&1, :newlines, newline) |
| 179 | + ) |
| 180 | + |
| 168 181 | {directive, updated_meta, children} |
| 169 182 | end |
| 170 183 | |
| 171 | - @doc """ |
| 172 | - "Fixes" the line numbers of nodes who have had their orders changed via sorting or other methods. |
| 173 | - This "fix" simply ensures that comments don't get wrecked as part of us moving AST nodes willy-nilly. |
| 184 | + def max_line([_ | _] = list), do: list |> List.last() |> max_line() |
| 174 185 | |
| 175 | - The fix is rather naive, and simply enforces the following property on the code: |
| 176 | - A given node must have a line number less than the following node. |
| 177 | - Et voila! Comments behave much better. |
| 186 | + def max_line(ast) do |
| 187 | + meta = |
| 188 | + case ast do |
| 189 | + {_, meta, _} -> |
| 190 | + meta |
| 178 191 | |
| 179 | - ## In Detail |
| 192 | + _ -> |
| 193 | + [] |
| 194 | + end |
| 180 195 | |
| 181 | - For example, given document |
| 196 | + if max_line = meta[:closing][:line] do |
| 197 | + max_line |
| 198 | + else |
| 199 | + {_, max_line} = |
| 200 | + Macro.prewalk(ast, 0, fn |
| 201 | + {_, meta, _} = ast, max -> {ast, max(meta[:line] || max, max)} |
| 202 | + ast, max -> {ast, max} |
| 203 | + end) |
| 182 204 | |
| 183 | - 1: defmodule ... |
| 184 | - 2: alias B |
| 185 | - 3: # this is foo |
| 186 | - 4: def foo ... |
| 187 | - 5: alias A |
| 188 | - |
| 189 | - Sorting aliases the ast node for would put `alias A` (line 5) before `alias B` (line 2). |
| 190 | - |
| 191 | - 1: defmodule ... |
| 192 | - 5: alias A |
| 193 | - 2: alias B |
| 194 | - 3: # this is foo |
| 195 | - 4: def foo ... |
| 196 | - |
| 197 | - Elixir's document algebra would then encounter `line: 5` and immediately dump all comments with `line <= 5`, |
| 198 | - meaning after running through the formatter we'd end up with |
| 199 | - |
| 200 | - 1: defmodule |
| 201 | - 2: # hi |
| 202 | - 3: # this is foo |
| 203 | - 4: alias A |
| 204 | - 5: alias B |
| 205 | - 6: |
| 206 | - 7: def foo ... |
| 207 | - |
| 208 | - This function fixes that by seeing that `alias A` has a higher line number than its following sibling `alias B` and so |
| 209 | - updates `alias A`'s line to be preceding `alias B`'s line. |
| 210 | - |
| 211 | - Running the results of this function through the formatter now no longer dumps the comments prematurely |
| 212 | - |
| 213 | - 1: defmodule ... |
| 214 | - 2: alias A |
| 215 | - 3: alias B |
| 216 | - 4: # this is foo |
| 217 | - 5: def foo ... |
| 218 | - """ |
| 219 | - def fix_line_numbers(nodes, nil), do: fix_line_numbers(nodes, 999_999) |
| 220 | - def fix_line_numbers(nodes, {_, meta, _}), do: fix_line_numbers(nodes, meta[:line]) |
| 221 | - def fix_line_numbers(nodes, max), do: nodes |> Enum.reverse() |> do_fix_lines(max, []) |
| 222 | - |
| 223 | - defp do_fix_lines([], _, acc), do: acc |
| 224 | - |
| 225 | - defp do_fix_lines([{_, meta, _} = node | nodes], max, acc) do |
| 226 | - line = meta[:line] |
| 227 | - |
| 228 | - # the -2 is just an ugly hack to leave room for one-liner comments and not hijack them. |
| 229 | - if line > max, |
| 230 | - do: do_fix_lines(nodes, max, [shift_line(node, max - line - 2) | acc]), |
| 231 | - else: do_fix_lines(nodes, line, [node | acc]) |
| 205 | + max_line |
| 206 | + end |
| 232 207 | end |
| 233 208 | |
| 234 | - # @TODO can i shortcut and just return end_of_expression[:line] when it's available? |
| 235 | - def max_line(ast) do |
| 236 | - {_, max_line} = |
| 237 | - Macro.prewalk(ast, 0, fn |
| 238 | - {_, meta, _} = ast, max -> {ast, max(meta[:line] || max, max)} |
| 239 | - ast, max -> {ast, max} |
| 240 | - end) |
| 209 | + def order_line_meta_and_comments(nodes, comments, first_line) do |
| 210 | + {nodes, comments, node_comments} = fix_lines(nodes, comments, first_line, [], []) |
| 211 | + {nodes, Enum.sort_by(comments ++ node_comments, & &1.line)} |
| 212 | + end |
| 241 213 | |
| 242 | - max_line |
| 214 | + defp fix_lines([], comments, _, node_acc, c_acc), do: {Enum.reverse(node_acc), comments, c_acc} |
| 215 | + |
| 216 | + defp fix_lines([node | nodes], comments, start_line, n_acc, c_acc) do |
| 217 | + meta = meta(node) |
| 218 | + line = meta[:line] |
| 219 | + last_line = meta[:end_of_expression][:line] || max_line(node) |
| 220 | + |
| 221 | + {node, node_comments, comments} = |
| 222 | + if start_line == line do |
| 223 | + {node, [], comments} |
| 224 | + else |
| 225 | + {mine, comments} = comments_for_lines(comments, line, last_line) |
| 226 | + line_with_comments = (List.first(mine)[:line] || line) - (List.first(mine)[:previous_eol_count] || 1) + 1 |
| 227 | + |
| 228 | + if line_with_comments == start_line do |
| 229 | + {node, mine, comments} |
| 230 | + else |
| 231 | + shift = start_line - line_with_comments |
| 232 | + # fix the node's line |
| 233 | + node = shift_line(node, shift) |
| 234 | + # fix the comment's line |
| 235 | + mine = Enum.map(mine, &%{&1 | line: &1.line + shift}) |
| 236 | + {node, mine, comments} |
| 237 | + end |
| 238 | + end |
| 239 | + |
| 240 | + meta = meta(node) |
| 241 | + # @TODO what about comments that were free floating between blocks? i'm just ignoring them and maybe always will... |
| 242 | + # kind of just want to shove them to the end though, so that they don't interrupt existing stanzas. |
| 243 | + # i think that's accomplishable by doing a final call above that finds all comments in the comments list that weren't moved |
| 244 | + # and which are in the range of start..finish and sets their lines to finish! |
| 245 | + last_line = meta[:end_of_expression][:line] || max_line(node) |
| 246 | + last_line = (meta[:end_of_expression][:newlines] || 1) + last_line |
| 247 | + fix_lines(nodes, comments, last_line, [node | n_acc], node_comments ++ c_acc) |
| 248 | + end |
| 249 | + |
| 250 | + # typical node |
| 251 | + def meta({_, meta, _}), do: meta |
| 252 | + # kwl tuple ala a: :b |
| 253 | + def meta({{_, meta, _}, _}), do: meta |
| 254 | + def meta(_), do: nil |
| 255 | + |
| 256 | + @doc """ |
| 257 | + Returns all comments "for" a node, including on the line before it. |
| 258 | + see `comments_for_lines` for more |
| 259 | + """ |
| 260 | + def comments_for_node({_, m, _} = node, comments) do |
| 261 | + last_line = m[:end_of_expression][:line] || max_line(node) |
| 262 | + comments_for_lines(comments, m[:line], last_line) |
| 263 | + end |
| 264 | + |
| 265 | + @doc """ |
| 266 | + Gets all comments in range start_line..last_line, and any comments immediately before start_line.s |
| 267 | + |
| 268 | + 1. code |
| 269 | + 2. # a |
| 270 | + 3. # b |
| 271 | + 4. code # c |
| 272 | + 5. # d |
| 273 | + 6. code |
| 274 | + 7. # e |
| 275 | + |
| 276 | + here, comments_for_lines(comments, 4, 6) is "a", "b", "c", "d" |
| 277 | + """ |
| 278 | + def comments_for_lines(comments, start_line, last_line) do |
| 279 | + comments |> Enum.reverse() |> comments_for_lines(start_line, last_line, [], []) |
| 280 | + end |
| 281 | + |
| 282 | + defp comments_for_lines(reversed_comments, start, last, match, acc) |
| 283 | + |
| 284 | + defp comments_for_lines([], _, _, match, acc), do: {Enum.reverse(match), acc} |
| 285 | + |
| 286 | + defp comments_for_lines([%{line: line} = comment | rev_comments], start, last, match, acc) do |
| 287 | + cond do |
| 288 | + # after our block - no match |
| 289 | + line > last -> comments_for_lines(rev_comments, start, last, match, [comment | acc]) |
| 290 | + # after start, before last -- it's a match! |
| 291 | + line >= start -> comments_for_lines(rev_comments, start, last, [comment | match], acc) |
| 292 | + # this is a comment immediately before start, which means it's modifying this block... |
| 293 | + # we count that as a match, and look above it to see if it's a multiline comment |
| 294 | + line == start - 1 -> comments_for_lines(rev_comments, start - 1, last, [comment | match], acc) |
| 295 | + # comment before start - we've thus iterated through all comments which could be in our range |
| 296 | + true -> {match, Enum.reverse(rev_comments, [comment | acc])} |
| 297 | + end |
| 243 298 | end |
| 244 299 | end |
Loading more files…