tensiondriven

tensiondriven

Mix formatter to remove unnecessary newlines?

I’ve looked around for information on this in the mix format source, custom mix formatters, Elixir style guides, and haven’t found anything, so please forgive me if this has been covered…

mix format does a great job of cleaning up unnecessary spaces in Elixir code and breaks long lines into multiple lines very well.

However, while it’s great at adding newlines when needed, there doesn’t seem to be any provision for removing unnecessary newlines.

I’m working on a team where there’s a tendency to use extensive pattern matching in function definitions, etc, which results in many multiline statements. When this code is edited and shortened, often the revised statements will fit on one line, but because the formatter doesn’t do this for us, I frequently see code that has unnecessary line breaks in it. The below should illustrate what I’m seeking. Note that the entirety of this code block has mix format applied:

  # Functions w/ long params get split into multiple lines (expected, desired):
  def function(
        very_long_parameter_that_causes_line_break1,
        very_long_parameter_that_causes_line_break2
      ) do
    # ...
  end

  # Shortening the long variable names does not result in the function definition being shortened: (i would like it to)
  def function(
        shortened_param1,
        shortened_param1
      ) do
    # ...
  end

  # Manually shortening the params works, but it is laborious:
  def function(shortened_param1, shortened_param1) do
    # ...
  end

  # This applies to structs/maps, too. I'd like these unnecessary newlines to be removed...
  def function() do
    #
    %{
      a: 1
    }
  end

  # ... so that after mix format, I have:
  def function() do
    %{a: 1}
  end

Is it possible to detect and remove un-necessary newlines using mix format? I started going down the path of writing my own formatter, but am not up on all the details of the Code module and am hoping there’s an easier way - or, if not, perhaps someone can outline an example formatter that I could write which would do this.

Most Liked

LostKobrakai

LostKobrakai

Iirc there’s a clear stance of the elixir team that the formatter is not meant to be a all purpose formatter. It’s meant to support elixir own development and therefore likely fits a lot of other elixir open source projects as well. But it just might not be the tool for enforcing custom or internal code styles.

josevalim

josevalim

Creator of Elixir

Exactly what @LostKobrakai said. The formatter would aggressively collapse before but then a lot of people complained that sometimes they would write newlines on purpose and the formatter collapsed (and “ruined”) them. So we started respecting the user choice when it comes to newlines.

This pretty much sums up most formatter discussions. x% people are happy it works in one way, y% people are not. :smiley: If we were to change it, now y% people are happy it works in one way, x% people are not.

sodapopcan

sodapopcan

Just in terms of reasoning, I believe it has to do with the ambiguity involved in defining “unnecessary”. For example, should the following be collapsed just because it fits in the line limit?

%{
  one: some_longer_function_name("some_arg", "some_other_arg"),
  two: "hello!"
}

That would look pretty terrible on one line (subjectively) and I believe it’s trying to allow for this type of thing. I’m personally totally cool with this as the diffs caused by tools like Prettier that put everything on a line it can make for some very painful diffs when those lines ultimately get broken up. The big downside, of course, is that you can’t just run the formatter to get stuff exactly as you want it.

LostKobrakai

LostKobrakai

The changes are not reversed because they not only happen because the formatter applied the changes, but also because a user might have written the code that way and the formatter will maintain some user preferences forward at the expense of not being able to “undo” all changes it does to code.

Marcus

Marcus

recode 0.2.0 has now the task SameLine. This task tries to get everything in one code line. The task is deactivated per default but the execution can be forced by mix recode --task SameLine. Keep in mind that there may be more changes than expected.
If someone has a better name for the task, please let me know.

The task can be configured with :skip and :ignore.
Imagine the following code snippet.

    test "my test" do
      x = %{
        foo: :fail
      }

      assert_raise RuntimeError, fn ->
        do_some(%{
          x: x,
          y: :foo
        })
      end
    end

recode changing this to:

    test "my test" do
      x = %{foo: :fail}

      assert_raise RuntimeError, fn -> do_some(%{x: x, y: :foo}) end
    end

and with ignore: :fn to:

    test "my test" do
      x = %{foo: :fail}

      assert_raise RuntimeError, fn ->
        do_some(%{x: x, y: :foo})
      end
    end

and with skip: :assert_raise to:

    test "my test" do
      x = %{foo: :fail}

      assert_raise RuntimeError, fn ->
        do_some(%{
          x: x,
          y: :foo
        })
      end
    end

Where Next?

Popular in Questions Top

Harrisonl
We have an ECS cluster with 4 services, where each task joins a single cluster, via discovery ECS discovery service. Currently when I de...
New
dokuzbir
Hello, I am trying to convert my lists to string without losing brackets.For start i have 3 map. They look like these buyer = %{ id: ...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
makeitrein
Hey all, just started picking up Elixir last week and am writing a scraper as a learning project. Baby step #1 is extracting the number ...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
chewm
Hi guys, nice to meet you to the whole forum, I’m new here, I’m trying to configure visual studio code for elixir, right now the intellis...
New
jc00ke
Expanding on this topic: https://forum.elixirforum.net/t/map-typespec-question/19217 Let’s say I have a map with required and optional k...
New
jay1
Why is it that the mnesia database isn’t the most preferred database for use in Elixir/Phoenix?
New

Other popular topics Top

Tee
can someone please explain to me how Enum.reduce works with maps
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
Jim
As a follow up to my earlier question: I have the code compiling and running but not getting a successful login from the rest server. ...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
WestKeys
Currently suffering from paralysis by [HTTP client] analysis. This is rather unusual in Elixirland as there tends to be consensus on the ...
New
josevalim
Hi everyone, One of the features added to Elixir early on to help integration with Erlang code was the idea of overridable function defi...
New
AstonJ
by Lance Halvorsen Elixir and Phoenix are generating tremendous excitement as an unbeatable platform for building modern web application...
460 27162 124
New

We're in Beta

About us Mission Statement