Is your feature request related to a problem? Please describe.
Chained if-else statements are being indented/formatted badly.
Describe the solution you'd like
Fix the formatting to make the alignment more clear.
Describe alternatives you've considered
Ignoring the recommended formatting isn't a suitable alternative.
Additional context
As an example:
if (x0) then
y0
else
if (x1) then
y1
else
if (x2) then
y2;
This is the wrong way to format this. The correct way (maintaining the silly if on new line) would be:
if (x0) then
y0
else
if (x1) then
y1
else
if (x2) then
y2;
Or even:
if (x0) then
y0
else
if (x1) then
y1
else
if (x2) then
y2;
Essentially, anything would be better than the current indenting.
Fully agree that the current way is annoying.
From my C/AL background I would add (and prefer) a third alternative
if (x0) then
y0
else if (x1) then
y1
else if (x2) then
y2;
If I had to choose form the above mentioned alternatives my preference would be the second one:
Or even:
if (x0) then y0 else if (x1) then y1 else if (x2) then y2;
Fully agree that the current way is annoying.
From my C/AL background I would add (and prefer) a third alternative
I agree, hence
(maintaining the silly if on new line)
Fully agree that the current way is annoying.
From my C/AL background I would add (and prefer) a third alternative
if (x0) then y0 else if (x1) then y1 else if (x2) then y2;
If I had to choose form the above mentioned alternatives my preference would be the second one:
Or even:
if (x0) then y0 else if (x1) then y1 else if (x2) then y2;
The first example is exactly what the formatter already does (given that this question was opened several months ago it could be out of date?)
If you look at c#, all conditionals are on the same line as the else
if (stringValue == string.Empty)
{
}
else if (someVar == "hello world")
{
}
If you put the if on a different line the auto-formatter doesn't do anything with it. I assume the style guidelines say to never do this as it makes code less readable.
if (someVar == string.Empty)
{
}
else
if (2 == 2)
{
}
The first example is exactly what the formatter already does (given that this question was opened several months ago it could be out of date?)
No, the formatter does not do that. It does this ugliness:
This is with version 4.0.198182 - potentially there may be something newer now? Seems sometimes there's newer extensions in the docker images, but I haven't checked since the mess BC15 created.
Essentially, if you have a situation where you need to check different conditions that are all mutually exclusive, you end up with a nested mess, even though they are all logically on the same level. You could use a "case true of" but that's old school nav ugly.
The first example is exactly what the formatter already does (given that this question was opened several months ago it could be out of date?)
No, the formatter does not do that. It does this ugliness:
This is with version 4.0.198182 - potentially there may be something newer now? Seems sometimes there's newer extensions in the docker images, but I haven't checked since the mess BC15 created.
Essentially, if you have a situation where you need to check different conditions that are all mutually exclusive, you end up with a nested mess, even though they are all logically on the same level. You could use a "case true of" but that's old school nav ugly.
Ah yes, you are right, it's funny, I never noticed this before, it's very rare I'd put conditionals in a stack like this.
I don't mind it so much since at least it indents the if
s under the else
s
The only thing I'd settle for is the else/if
being on the same line - any other indentation under an else just doesn't read very well.
Yes, the else and the if should be on the same line. If I remember my pascal correctly, that is standard for pascal as well. I think the end got it's own line in pascal though.
This formatting just breaks the normal paradigm for no real logical reason. It structures it as though the first else was "end else begin" nesting the ifs instead of an else if. Logically it makes no difference. Visually the difference is huge.
Can someone please explain to me what is wrong about the very first mentioned version? Every if statement has only one else - maximum. The other elses belong to other ifs.
I really see no reason to "pretend" the other elses belong to the very first if.
Can someone please explain to me what is wrong about the very first mentioned version? Every if statement has only one else - maximum. The other elses belong to other ifs.
I really see no reason to "pretend" the other elses belong to the very first if.
This isn't about pretending anything, it's about fitting the format to the logic developers use. It's about matching the "mother language" of pascal. From a technical perspective, what the formatter does is correct, but horrendously ugly. Yes, each if has one else and everything nests from there, but I would argue that this is the standard developers will use (here as images because github didn't respect the whitespace):
I know what this translates into, and that's perfectly fine, but the above is easier to read than:
The more conditions you have, the more the problem is compounded. Seeing all the conditions within one alignment allows us to very quickly see what the code is evaluating vs tracing it with our eyes.
I've exaggerated the statements for demonstration purposes. You're unlikely to hit this many at any one point but I do believe it an effective visual example. This can't always be a case statement unless you do the awful case true of.
Thanks @mjmatthiesen
When you have an "If - else if - else if - else ..." the last else block has drifted horizontally away from the original if statement that started it.
Block-structured programming languages should align the blocks by how many levels they go.
Like I said in my issue:
if p then begin
if x then begin
a;
end;
end else if q then begin
if y then begin
b;
end;
end else if r then begin
if z then begin
c;
end;
end;
should not become:
if p then begin
if x then begin
a;
end;
end else
if q then begin
if y then begin
b;
end;
end else
if r then begin
if z then begin
c;
end;
end;
r is the result of both q and p being false (so it depends on them both), but has vertically no relation to p anymore because of the indenting made by the AL extension.
Bracket-based if-else blocks would look like this in other languages (with similar indentation logic):
if (p) {
if (x) {
a;
}
} else {
if (q) {
if (y) {
b;
}
} else {
if (r) {
if (z) {
c;
}
}
}
}
while this is maybe what you're trying to write:
if (p) {
if (x) {
a;
}
} else if (q) {
if (y) {
b;
}
} else if (r) {
if (z) {
c;
}
}
Personally I don't get the "enhancement" label of al-formatting on this issue, this is clearly a bug in al-formatting. You start with an if-statement that just drifts off if you're relying on an if - else if - else. This is a step backwards in making complicated conditionals more readable. Just take a look at how vscode is handling bracket highlighting for begin-end:
begin only seems to match with an "end;" but ignores "end"... just try it yourselves.
Every logic that doesn't include a simple "if p then a else b;" looks very odd after the indentation.
You'll probably see a lot more developers stumble upon this issue.
To those developers:
if - else if - else is broken in AL - you can only use if-else, so the only structure that AL formatter will leave alone is by wrapping each else with a begin-end:
if p then begin
if x then begin
a;
end;
end else begin
if q then begin
if y then begin
b;
end;
end else begin
if r then begin
if z then begin
c;
end;
end;
end;
end;
but the highlighting is still broken (and you still lose vertical-relativity for conditionals and nesting).
Hopefully this long comment is of some help and hopefully this issue will be resolved.
This is appearing more often as I go through more codeunits:
(this is from a codeunit which has been in production for many years in C/AL but formatting indents everything more and more to the right in AL)
This is appearing more often as I go through more codeunits:
(this is from a codeunit which has been in production for many years in C/AL but formatting indents everything more and more to the left in AL)
Bad programming I would say.
@bjarkihall
The reason I have this tagged as an enhancement and not a bug is because there is no "else if" structure. Each if can only have one else, everything after that is syntactical sugar. Right now the AL formatter is treating this in its literal sense, i.e. one if = one else. Every else if is actually contained in the else.
What the formatter is doing isn't wrong, it's just visually bad and undoing the syntactical sugar we had in CAL.
This is appearing more often as I go through more codeunits:
(this is from a codeunit which has been in production for many years in C/AL but formatting indents everything more and more to the left in AL)
You gotta wonder why you have 150 nested conditionals...
Agreeing with Luc here, looks like you need to refactor!
The formatting can be annoying but most of the time you don't need to be more than a couple of levels nested.
This is not my code but a common drift I'm seeing in legacy codeunits and solutions converted from C/AL to AL.
@charlespockert this isn't 150 nested conditionals, this happens e.g. when a procedure is declared with an if-else-if-else-... chain, similar to a case-of (switch) statement (which is not equivalent to else-if chaining) and while this is an extreme example, I'm surprised there aren't more encounters with this issue.
@mjmatthiesen yes, this is functionally the same and both C/AL and AL compilers treat it in the same way. However, regarding the comment "What the formatter is doing isn't wrong, it's just visually bad", what is the point of a visually bad formatter?
I absolutely agree. I think it's bad and needs to be fixed, but I don't think it's a bug.
MS recommend to use case true of
for such situations, but that's not intuitive.
Many other, more mainstream languages can't do that kind of case
/switch
but _can_ and do advise to format if
/else if
properly so that it visually reflects the code's logic.
So it sort of feels that advising case true of
is a poor workaround for bad formatting.
Plus, that comes with its own disadvantages due to how the options must be on their own level of indentation, if they're compound statements then they go another level in due to begin
/end
getting their own lines/levels, etc. It ends up being worse than a well-formatted if
/else
would be!
This is not my code but a common drift I'm seeing in legacy codeunits and solutions converted from C/AL to AL.
@charlespockert this isn't 150 nested conditionals, this happens e.g. when a procedure is declared with an if-else-if-else-... chain, similar to a case-of (switch) statement (which is not equivalent to else-if chaining) and while this is an extreme example, I'm surprised there aren't more encounters with this issue.
@mjmatthiesen yes, this is functionally the same and both C/AL and AL compilers treat it in the same way. However, regarding the comment "What the formatter is doing isn't wrong, it's just visually bad", what is the point of a visually bad formatter?
Yes sorry, I shouldn't have said nested, I guess I meant "chained".
Whilst I can agree that it could do with being fixed as it's not what I'd expect, I personally wouldn't put it high on the priority list - but it's interesting that some people are running into this situation but others aren't - it just goes to show a variety of different programming styles.
I don't think I've used a string of if/else
longer than say 2 conditional branches in a long time now.
I use case
statements because they just look nicer/feel more appropriate for this kind of situation.
I still stand by the refactor comment - if you need to evaluate 20+ conditions in a row in a single code block then maybe the code needs changing?
A case statement is a subset of an if statement, sometimes you can use it (if you're always comparing the same variable in all of the branches) and it looks cleaner, but in other cases you would like to use else-if.
The code really looked okay in C/AL and was properly formatted and readable, but I guess most code can always be refactored, but that's not the point. These kind of scenarios happen over many years as more cases are added by various people.
Just google how to refactor long else-if statements. The cleanest methods use switch/case if possible or end up using grouping/subtypes, which is only possible for new cases and some solutions use reflection, which AL doesn't really support.
Either way, you always end up implementing the 20 cases in AL, so do you create 20 "handled" pattern single-level if-statements, 20 new similar one-line single-use handler-functions or 20 handler-codeunits? What do you prefer? Some code will just inevitably become ugly, which is not a result you want from a formatter.
Else-if is just a basic programming language feature, which AL supports but formats poorly.
Let's say the same argument I'm getting here was being used against the else keyword when AL was being made and it would be formatted in a weird way (while still working while executing):
if a then
DoSomething()
else DoSomethingElse();
You could argue, that else is just the same as if not - which had a better formatting and uses fewer keywords to remember, so everyone started doing this:
if a then
DoSomething();
if not a then
DoSomethingElse();
Much cleaner and you can have semicolons in both of the cases!
Maybe this example is silly but you get where I'm coming from, even though something evaluates to the same thing functionally, it doesn't mean it's the same thing and the other one should remain weird because it didn't get proper implementation - let alone suggesting everyone to refactor their whole code bases to stop using "else", instead of just fixing (or turning off) the bad formatting for it. You could use other equal cases like "if a then if b then" is the same as "if a and b then", should one of them be poorly formatted and should you waste your time refactoring everything from one to the another?
I don't think a formatter bug is a good reason for code refactoring, I use case-of where possible but every time it's not possible or when I encounter legacy code which suffer from this weirdness I wonder why MS don't hate running into it themselves.
First example I found in the MS Base App (v15 and sorry for the lack of highlighting, AL extension kept crashing on large files) was e.g.:
VAT Rate Change Conversion codeunit:
Item tracing Mgt codeunit:
Item Jnl.-Post Line codeunit:
and even though this can be replaced with case-of this probably looked differently in C/AL (Excel Data Migrator):
They even created a codecop rule and a code action to refactor else-if to case-of (which can't be run as a bulk-fix, I wonder why...) and implemented a recent feature of single-line variable type declarations, which I don't recall anyone asking for - even though I really like the idea of the language growing and becoming more feature-rich - but not prioritizing getting basic logic expressions from formatting properly really makes newcomers to the language skeptical about it.
Most helpful comment
Fully agree that the current way is annoying.
From my C/AL background I would add (and prefer) a third alternative
If I had to choose form the above mentioned alternatives my preference would be the second one: