Skip to content

Consistent indentations for conditional operators#10187

Merged
thorn0 merged 35 commits intoprettier:mainfrom
sosukesuzuki:fix-8840
Feb 7, 2021
Merged

Consistent indentations for conditional operators#10187
thorn0 merged 35 commits intoprettier:mainfrom
sosukesuzuki:fix-8840

Conversation

@sosukesuzuki
Copy link
Copy Markdown
Contributor

@sosukesuzuki sosukesuzuki commented Jan 31, 2021

Description

Fixes #8840
Fixes #4976

Checklist

  • I’ve added tests to confirm my change works.
  • (If the change is user-facing) I’ve added my changes to changelog_unreleased/*/XXXX.md file following changelog_unreleased/TEMPLATE.md.
  • I’ve read the contributing guidelines.

Try the playground for this PR

@thorn0
Copy link
Copy Markdown
Member

thorn0 commented Feb 2, 2021

What about binary expressions? I don't think this change is intended for this PR.

Prettier pr-10187
Playground link

Output:

bifornCringerMoshedPerplexSawder =
  askTrovenaBeenaDependsRowans +
  ((
    glimseGlyphsHazardNoopsTieTie === 0
      ? averredBathersBoxroomBuggyNurl
      : anodyneCondosMalateOverateRetinol
  ) as AnnularCooeedSplicesWalksWayWay);

bifornCringerMoshedPerplexSawder =
  askTrovenaBeenaDependsRowans +
  ((
    glimseGlyphsHazardNoopsTieTie === 0 &&
    kochabCooieGameOnOboleUnweave === Math.PI
      ? averredBathersBoxroomBuggyNurl
      : anodyneCondosMalateOverateRetinol
  ) as AnnularCooeedSplicesWalksWayWay);

Prettier 2.2.1
Playground link

Output:

bifornCringerMoshedPerplexSawder =
  askTrovenaBeenaDependsRowans +
  ((glimseGlyphsHazardNoopsTieTie === 0
    ? averredBathersBoxroomBuggyNurl
    : anodyneCondosMalateOverateRetinol) as AnnularCooeedSplicesWalksWayWay);

bifornCringerMoshedPerplexSawder =
  askTrovenaBeenaDependsRowans +
  ((glimseGlyphsHazardNoopsTieTie === 0 &&
  kochabCooieGameOnOboleUnweave === Math.PI
    ? averredBathersBoxroomBuggyNurl
    : anodyneCondosMalateOverateRetinol) as AnnularCooeedSplicesWalksWayWay);

) {
let memberChainingRootNodeName = parentName;
let count = 0;
while (memberChainingRootNodeName === "object") {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Member chains also include calls:
Prettier pr-10187
Playground link

--parser typescript

Input:

bifornCringerMoshedPerplexSawder = (
  glimseGlyphsHazardNoopsTieTie === 0 &&
  kochabCooieGameOnOboleUnweave === Math.PI
    ? averredBathersBoxroomBuggyNurl
    : anodyneCondosMalateOverateRetinol
).annularCooeedSplicesWalksWayWay.annularCooeedSplicesWalksWayWay(
  annularCooeedSplicesWalksWayWay
).annularCooeedSplicesWalksWayWay();

Output:

bifornCringerMoshedPerplexSawder = (glimseGlyphsHazardNoopsTieTie === 0 &&
kochabCooieGameOnOboleUnweave === Math.PI
  ? averredBathersBoxroomBuggyNurl
  : anodyneCondosMalateOverateRetinol
).annularCooeedSplicesWalksWayWay
  .annularCooeedSplicesWalksWayWay(annularCooeedSplicesWalksWayWay)
  .annularCooeedSplicesWalksWayWay();

@sosukesuzuki sosukesuzuki changed the title Fix formatting for ternary operators with TypeScript as. Consistent indentations for conditional operators Feb 3, 2021
@sosukesuzuki sosukesuzuki requested review from fisker and thorn0 February 3, 2021 19:56
@thorn0
Copy link
Copy Markdown
Member

thorn0 commented Feb 3, 2021

Also member chains might include TSNonNullExpression.

return printed;
}

const rightSideNodeNamesSet = new Set(["right", "init", "argument"]);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I didn't misunderstand this, we are checking NodeA is NodeB's .right or .init, but not checking NodeB's type. I don't like this part, this may lead to unexpect cases.

For example argument, I guess you mean to check argument of ReturnStatement/YieldExpression etc, let's list them.

* )(arguments);
*/
if (
parent.type === "CallExpression" &&
Copy link
Copy Markdown
Member

@fisker fisker Feb 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OptionalCallExpression too?

Prettier pr-10187
Playground link

--parser babel

Input:

function foo22() {
  return (
    coooooooooooooooooooooooooooooooooooooooooooooooooooond
      ? baaaaaaaaaaaaaaaaaaaaar
      : baaaaaaaaaaaaaaaaaaaaaz
  )(Fooooooooooo.Fooooooooooo);

  return (
    coooooooooooooooooooooooooooooooooooooooooooooooooooond
      ? baaaaaaaaaaaaaaaaaaaaar
      : baaaaaaaaaaaaaaaaaaaaaz
  )?.(Fooooooooooo.Fooooooooooo);
}

Output:

function foo22() {
  return (
    coooooooooooooooooooooooooooooooooooooooooooooooooooond
      ? baaaaaaaaaaaaaaaaaaaaar
      : baaaaaaaaaaaaaaaaaaaaaz
  )(Fooooooooooo.Fooooooooooo);

  return (coooooooooooooooooooooooooooooooooooooooooooooooooooond
    ? baaaaaaaaaaaaaaaaaaaaar
    : baaaaaaaaaaaaaaaaaaaaaz)?.(Fooooooooooo.Fooooooooooo);
}

if (
(parent.type === "CallExpression" ||
parent.type === "OptionalCallExpression") &&
checkAncestor(parent) &&
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't you reuse ancestorCount here instead of climbing up the path one more time in getChainRoot?

Also what about the case where parent.type === "TSNonNullExpression"?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant to write this under parent.type === "MemberExpression" || parent.type === "OptionalMemberExpression". But maybe you should do the chain root check here too? After all, the search for ancestor above considers calls chain elements.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to get rid of the getChainRoot function by reusing the ancestorCount, thanks!

return true;
}

const chainRoot = path.getParentNode(ancestorCount - 1);
Copy link
Copy Markdown
Member

@fisker fisker Feb 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The rest part in this function seems just CallExpression.callee, MemberExpression.object, and TSNonNullExpression.object? Code like this should work?

if (
  isChainElement(parent) &&
  checkAncestor(chainRoot) &&
  (
    (isCallExpression() && name === "callee") ||
    (isMemberExpression() && name === "object") ||
    (parent.type === 'TSNonNullExpression' && name === "object") ||
  )
)

Note: name === "object" was not checking, we should add tests for them.

@fisker
Copy link
Copy Markdown
Member

fisker commented Feb 5, 2021


const chainRoot = path.getParentNode(ancestorCount - 1);
if (
isChainElement(parent) &&
Copy link
Copy Markdown
Member

@fisker fisker Feb 5, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isChainElement check is not needed anymore, following codition already ensure it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And please tweak the conditions order a little bit. check name first and check parent first.

* : second
* ) as SomeType;
*/
if (parent.type === "TSAsExpression" && checkAncestor(parent)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's merge TSAsExpression and NewExpression too,

  if (
    (
		(name === "callee" && parent.type === "NewExpression") ||
		(name === "expression" && parent.type === "TSAsExpression") 
	) &&
    checkAncestor(parent)
  )

Once again, name === "expression" not checked, TSAsExpression has two children, seems typeAnnotation can't be ConditionalExpression, but let's add it to be safer.

Comment on lines 222 to 230
const chainRoot = path.getParentNode(ancestorCount - 1);
if (
isChainElement(parent) &&
checkAncestor(chainRoot) &&
((isCallExpression(parent) && name === "callee") ||
(isMemberExpression(parent) && name === "object") ||
(parent.type === "TSNonNullExpression" && name === "expression"))
((name === "callee" && isCallExpression(parent)) ||
(name === "object" && isMemberExpression(parent)) ||
(name === "expression" && parent.type === "TSNonNullExpression"))
) {
return true;
}
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const chainRoot = path.getParentNode(ancestorCount - 1);
if (
isChainElement(parent) &&
checkAncestor(chainRoot) &&
((isCallExpression(parent) && name === "callee") ||
(isMemberExpression(parent) && name === "object") ||
(parent.type === "TSNonNullExpression" && name === "expression"))
((name === "callee" && isCallExpression(parent)) ||
(name === "object" && isMemberExpression(parent)) ||
(name === "expression" && parent.type === "TSNonNullExpression"))
) {
return true;
}
if (
((name === "callee" && isCallExpression(parent)) ||
(name === "object" && isMemberExpression(parent)) ||
(name === "expression" && parent.type === "TSNonNullExpression")) &&
checkAncestor(path.getParentNode(ancestorCount - 1))
) {
return true;
}

This reduces one getParentNode() call.

@thorn0
Copy link
Copy Markdown
Member

thorn0 commented Feb 5, 2021

thorn0 Do you think this fixes your old issue #4976?

@fisker For conditionals in member expressions, yes, it looks good.

@sosukesuzuki
Copy link
Copy Markdown
Contributor Author

Can we merge this?

@thorn0 thorn0 merged commit cc1f054 into prettier:main Feb 7, 2021
@sosukesuzuki sosukesuzuki deleted the fix-8840 branch February 7, 2021 15:37
@thorn0
Copy link
Copy Markdown
Member

thorn0 commented Feb 7, 2021

Oops. This inconsistency doesn't seem to be intended.

Prettier pr-10187
Playground link

--parser babel

Input:

bifornCringerMoshedPerplexSawder = fn((
  glimseGlyphsHazardNoopsTieTie === 0
    ? averredBathersBoxroomBuggyNurl
    : anodyneCondosMalateOverateRetinol
).prop);

fn((
  glimseGlyphsHazardNoopsTieTie === 0
    ? averredBathersBoxroomBuggyNurl
    : anodyneCondosMalateOverateRetinol
).prop);

Output:

bifornCringerMoshedPerplexSawder = fn(
  (
    glimseGlyphsHazardNoopsTieTie === 0
      ? averredBathersBoxroomBuggyNurl
      : anodyneCondosMalateOverateRetinol
  ).prop
);

fn(
  (glimseGlyphsHazardNoopsTieTie === 0
    ? averredBathersBoxroomBuggyNurl
    : anodyneCondosMalateOverateRetinol
  ).prop
);

@sosukesuzuki
Copy link
Copy Markdown
Contributor Author

Ooops. I didn't notice, I'll create new issue for this.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 8, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Ugly formatting of ternary inside a TS as assertion Invalid indentation: return (foo ? bar1 : bar2).baz()

4 participants