Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Formatter does not indent case and if when the result is explicitly typed #15421

Open
BigBoyBarney opened this issue Feb 6, 2025 · 8 comments · May be fixed by #15429
Open

Formatter does not indent case and if when the result is explicitly typed #15421

BigBoyBarney opened this issue Feb 6, 2025 · 8 comments · May be fixed by #15429
Labels
kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:tools:formatter

Comments

@BigBoyBarney
Copy link
Contributor

Hi!

I saw in #13002 that some people advocate for the removal of alignment in the formatter, which I personally think would be horrible, as I really like alignment, but that's besides the point.

The following cases work as expected, with nicely aligned if-else and case-when blocks.

x = if rand(2) == 1
      "One"
    else
      "Zero"
    end

x = case rand(2)
    when 0
      "Zero"
    when 1
      "One"
    end

If, however, the result, in this case x is explicitly typed, the formatter forces a 0 indent

x : String = if rand(2) == 1
  "One"
else
  "Zero"
end

x : String = case rand(2)
when 0
  "Zero"
when 1
  "One"
end
@BigBoyBarney BigBoyBarney added the kind:bug A bug in the code. Does not apply to documentation, specs, etc. label Feb 6, 2025
@BigBoyBarney
Copy link
Contributor Author

The following is indented incorrectly as well:

@[Flags]
enum Snorlax
  One
  Two
end

def test
  one = Snorlax::One

  one | case
  when true
    Snorlax::Two
  else
    Snorlax::Two
  end
end

p! test

Is this the same underlying issue, or should I open a new one?

@straight-shoota
Copy link
Member

I think the underlying issue is essentially the same, but the fix isn't
Since #15429 is already out (and doesn't fix this case), it should be a separate issue.

@straight-shoota
Copy link
Member

I'm not entirely convinced that this is a change for the better: See https://github.com/crystal-lang/crystal/pull/15429/files#diff-12c854dbe6de0cbc0ce7247ddca403639efc68ea3af0a6166eca69fb9fb60340

diff --git a/src/llvm/abi/aarch64.cr b/src/llvm/abi/aarch64.cr
index 4fdbe9f0979e..3a2600b849a9 100644
--- a/src/llvm/abi/aarch64.cr
+++ b/src/llvm/abi/aarch64.cr
@@ -19,15 +19,15 @@ class LLVM::ABI::AArch64 < LLVM::ABI
 
   def homogeneous_aggregate?(type)
     homog_agg : {Type, UInt64}? = case type.kind
-    when Type::Kind::Float
-      return {type, 1_u64}
-    when Type::Kind::Double
-      return {type, 1_u64}
-    when Type::Kind::Array
-      check_array(type)
-    when Type::Kind::Struct
-      check_struct(type)
-    end
+                                  when Type::Kind::Float
+                                    return {type, 1_u64}
+                                  when Type::Kind::Double
+                                    return {type, 1_u64}
+                                  when Type::Kind::Array
+                                    check_array(type)
+                                  when Type::Kind::Struct
+                                    check_struct(type)
+                                  end
 
     # Ensure we have at most four uniquely addressable members
     if homog_agg

This alignment on the column of the case keyword looks nice when it's just small indent from x : String. But a larger assingment target like homog_agg : {Type, UInt64}? pushes the following lines quite deep. It gets even worse when the branches themselves have longer line lengths.

This isn't ideal. I'm wondering if we shouldn't change the indentation of conditionals to be not aligned on the first keyword.
The formatter originally did this for all multi-line expressions in an assignment, but that was later reduced to only conditionals (#1773). Perhaps we should consider changing it for conditionals as well? This would also address the iconsistency critized in #15480.

@HertzDevil
Copy link
Contributor

If you look at the Rubocop style guide, it recommends putting the entire expression on a new line with 2 spaces for indentation. IIRC Elixir's formatter also works like that. It has the extra benefit that indentation won't break formatting if the left-hand side of the assignment has an even number of characters:

xx = if 123
       456 # 7 spaces
     end   # 5 spaces

IDEs often insert or remove only 1 space character when indenting or outdenting those lines.

@BigBoyBarney
Copy link
Contributor Author

BigBoyBarney commented Feb 27, 2025

I fully agree with Rubocop's style guide.

result = if some_cond
  calc_something
else
  calc_something_else
end

This general indentation is horrendous for readability in all cases.
The argument that indenting the lines to the keyword after an assignment can lead to extreme indentations also has merit. It's readable, but unsightly.

Putting it on a new line and indenting 2 whitespaces seems like an overall excellent solution, but I might not want to do that for short if-else assignments, because it adds an extra line. (personal preference)

Currently the formatter allows both of these:

@user_status.selected =
  case anime_overview_data.user_status
  in .watching?      then 0_u32
  in .plan_to_watch? then 1_u32
  in .completed?     then 2_u32
  in .paused?        then 3_u32
  in .dropped?       then 4_u32
  end

@user_status.selected = case anime_overview_data.user_status
                        in .watching?      then 0_u32
                        in .plan_to_watch? then 1_u32
                        in .completed?     then 2_u32
                        in .paused?        then 3_u32
                        in .dropped?       then 4_u32
                        end

Which I think is excellent. It allows the user to decide whether they consider a given indentation "too much". This works for case and if, but not for begin. Consistency here would be very welcome I think.

@ysbaddaden
Copy link
Contributor

I always skip a line when I assign the result of a multiline expression to avoid this exact too-far right alignment.

It wouldn't mind the formatter doing it automatically.

@BigBoyBarney
Copy link
Contributor Author

BigBoyBarney commented Feb 27, 2025

The issue with that is that it's very opinionated for short assignments

x = case @size
    when .small?
      1
    when .medium?
      2
    when .large?
      3
    end

I wouldn't really want this moved to the next line, if possible, as this looks a bit silly.

x =
  case @size
  when .small?
    1
  when .medium?
    2
  when .large?
    3
  end

I don't hate it, but allowing the user to choose when to do that would be excellent. Which is how it works with if-else and case currently.

@mverzilli
Copy link

I'm with #15421 (comment)

The issue with that is that it's very opinionated for short assignments

We could do it only when the right hand side of the assignment is beyond a given column...

The main issue is that in previous discussions there seemed to be consensus in that the formatter shouldn't change line breaks, which would make this solution not acceptable.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind:bug A bug in the code. Does not apply to documentation, specs, etc. topic:tools:formatter
Projects
None yet
5 participants