Skip to content

fix(javascript): ternary with --use-tabs#3745

Merged
ikatyang merged 13 commits intoprettier:masterfrom
ikatyang:fix/javascript-ternary-use-tabs-2
Jan 26, 2018
Merged

fix(javascript): ternary with --use-tabs#3745
ikatyang merged 13 commits intoprettier:masterfrom
ikatyang:fix/javascript-ternary-use-tabs-2

Conversation

@ikatyang
Copy link
Copy Markdown
Member

@ikatyang ikatyang commented Jan 15, 2018

Rewrite #3277, fixes #2771.

Rules:

Example:

  • before
    • tabWidth = 2
      • <tab><2 space><tab><2 space> -> <tab><tab><tab><tab>
      • <tab><4 space><tab><2 space> -> <tab><tab><tab><tab>
    • tabWidth = 4
      • <tab><2 space><tab><2 space> -> <tab><tab><tab><tab>
      • <tab><4 space><tab><2 space> -> <tab><tab><tab><tab>
  • after
    • tabWidth = 2
      • <tab><2 space><tab><2 space> -> <tab><tab><tab><2 space>
      • <tab><4 space><tab><2 space> -> <tab><tab><tab><2 space>
    • tabWidth = 4
      • <tab><2 space><tab><2 space> -> <tab><tab><tab><2 space>
      • <tab><4 space><tab><2 space> -> <tab><tab><tab><2 space>

P.S. align with string n is not affected.

Diff:

{ useTabs: true, tabWidth: 2 | 4 }
image


Prettier pr-3745
Playground link

--tab-width 4
--use-tabs

Input:

a
    ? {
        a: 0
      }
    : {
        a: {
             a: 0
           }
            ? {
                a: 0
              }
            : {
                y: {
                    a: 0
                }
                    ? {
                        a: 0
                    }
                    : {
                        a: 0
                    }
            }
      }

Output:

a
	? {
			a: 0
	  }
	: {
			a: {
				a: 0
			}
				? {
						a: 0
				  }
				: {
						y: {
							a: 0
						}
							? {
									a: 0
							  }
							: {
									a: 0
							  }
				  }
	  };

old
  • before
    • tabWidth = 2
      • <tab><2 space><tab><2 space> -> <tab><tab><tab><tab>
      • <tab><4 space><tab><2 space> -> <tab><tab><tab><tab>
    • tabWidth = 4
      • <tab><2 space><tab><2 space> -> <tab><tab><tab><tab>
      • <tab><4 space><tab><2 space> -> <tab><tab><tab><tab>
  • after
    • tabWidth = 2
      • <tab><2 space><tab><2 space> -> <tab><tab><tab><2 space>
      • <tab><4 space><tab><2 space> -> <tab><tab><tab><tab><2 space>
    • tabWidth = 4
      • <tab><2 space><tab><2 space> -> <tab><tab><2 space>
      • <tab><4 space><tab><2 space> -> <tab><tab><tab><2 space>

Diff:

{ useTabs: true, tabWidth: 2 }
image

{ useTabs: true, tabWidth: 4 }
image


Prettier pr-3745
Playground link

--tab-width 4
--use-tabs

Input:

a
    ? {
        a: 0
      }
    : {
        a: {
             a: 0
           }
            ? {
                a: 0
              }
            : {
                y: {
                    a: 0
                }
                    ? {
                        a: 0
                    }
                    : {
                        a: 0
                    }
            }
      }

Output:

a
	? {
		a: 0
	  }
	: {
		a: {
			a: 0
		}
			? {
				a: 0
			  }
			: {
				y: {
					a: 0
				}
					? {
						a: 0
					  }
					: {
						a: 0
					  }
			  }
	  };

@ikatyang
Copy link
Copy Markdown
Member Author

Ref: previous discussion #3277 (comment)

We have to read through #1026 (the PR that added tabs support) again, because as far as I remember all of this was discussed back then.

When using spaces, Prettier indents with 2 spaces + 1 indent level. When using tabs, there are three ways to go:

  • Indent with one tab.
  • Indent with two tabs.
  • Indent with 2 spaces + 1 tab.

As far as I remember, all of those were discussed with pros and cons, but I don't have the time right now to dig into it.

? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Copy link
Copy Markdown
Member Author

@ikatyang ikatyang Jan 16, 2018

Choose a reason for hiding this comment

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

We need to use markAsRoot and dedentToRoot from #3676 to re-indent with proper spaces/tab.

@ikatyang ikatyang added the status:wip Pull requests that are a work in progress and should not be merged label Jan 16, 2018
@rhengles
Copy link
Copy Markdown
Contributor

rhengles commented Jan 16, 2018

@ikatyang I'm amazed at the effort you put into this. I recognize that the output indeed looks very nice.

However, the reason the code is the way it is is because <tabs> do not have an intrinsic length.

It looks very nice when you're formatting the code in your machine, with your editor tab length.

But the purpose of tabs is to be tab length agnostic.

When you share your tab indented code with another user who have an different tab length, the code will no longer be perfectly aligned.

That's why it is better to indent only with tabs, and to let the code be a little misaligned.

@ikatyang
Copy link
Copy Markdown
Member Author

It seems the trailing part is fine but the middle part should be still transformed into tabs per align?

@rhengles
Copy link
Copy Markdown
Contributor

@ikatyang Ah, the trailing part. It's simple when all you have inside a ternary is an literal, variable or object with simple properties (again, literal and variables).

But it gets messy very fast - inside the object might be another object, an array or even a function, with endless combinations.

That's why I think in useTabs mode, every indentation level must be a <tab>.

@ikatyang
Copy link
Copy Markdown
Member Author

Removed the middle part and fixed the nested ternary case, @rhengles I think it should be fine now. If not, could you provide some examples so that I can improve it?


Prettier pr-3745
Playground link

--tab-width 4
--use-tabs

Input:

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
?
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
?
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
?
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa


a
    ? {
        a: 0
      }
    : {
        a: {
             a: 0
           }
            ? {
                a: 0
              }
            : {
                y: {
                    a: 0
                }
                    ? {
                        a: 0
                    }
                    : {
                        a: 0
                    }
            }
      }

Output:

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
	? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
		? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
			? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
			: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
		: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
	: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa;

a
	? {
			a: 0
	  }
	: {
			a: {
				a: 0
			}
				? {
						a: 0
				  }
				: {
						y: {
							a: 0
						}
							? {
									a: 0
							  }
							: {
									a: 0
							  }
				  }
	  };

@ikatyang ikatyang removed the status:wip Pull requests that are a work in progress and should not be merged label Jan 17, 2018
@rhengles
Copy link
Copy Markdown
Contributor

rhengles commented Jan 17, 2018

@ikatyang Ah, I see it now. You only ever put spaces on the closing bracket of an object who is a direct child of the result part of a ternary expression.

Just so we have a comprehensive test case to ensure no regressions, can you add this test case?
Of course no one should ever write code like this, but we have to make sure nothing breaks in the future 😃

a
	? {
			a: function() {
				return a
					? {
							a: [
								a
									? {
											a: 0,
											b: [
												a
													? [
															0,
															1
													  ]
													: []
											]
									  }
									: [
											[
												0,
												{
													a: 0
												},
												a
													? 0
													: 1
											],
											function() {
												return a
													? {
															a: 0
													  }
													: [
															{
																a: 0
															},
															{}
													  ];
											}
									  ]
							]
					  }
					: [
							a
								? function() {
										a
											? a(
													a
														? {
																a: a(
																	{
																		a: 0
																	}
																)
														  }
														: [
																0,
																a(),
																a(
																	a(),
																	{
																		a: 0
																	},
																	a
																		? a()
																		: a(
																				{
																					a: 0
																				}
																		  )
																),
																a()
																	? {
																			a: a(),
																			b: []
																	  }
																	: {}
														  ]
											  )
											: a();
										a(
											a()
												? {
														a: 0
												  }
												: (function(a) {
														return a()
															? [
																	{
																		a: 0,
																		b: a()
																	}
															  ]
															: a(
																	[
																		a
																			? {
																					a: 0
																			  }
																			: {},
																		{
																			a: 0
																		}
																	]
															  );
												  })(
														a
															? function(a) {
																	return function() {
																		return 0;
																	};
															  }
															: function(a) {
																	return function() {
																		return 1;
																	}
															  }
												  )
										);
								  }
								: function() {
										
								  }
					  ];
			}
	  }
	: a;

Copy link
Copy Markdown
Collaborator

@duailibe duailibe left a comment

Choose a reason for hiding this comment

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

I'm not 100% sure how everything works, but code and snapshots LGTM (though I don't use tabs so I'm not sure what to expect :))

@ikatyang ikatyang merged commit 03292a6 into prettier:master Jan 26, 2018
@ikatyang ikatyang deleted the fix/javascript-ternary-use-tabs-2 branch January 26, 2018 02:02
@wmertens
Copy link
Copy Markdown

At first I was like 🤔 and then I was like 😕 but it kinda looks ok so I'll just kill that eslint mixed spaces rule and be like 🤷‍♂️

@jseminck
Copy link
Copy Markdown

jseminck commented Mar 16, 2018

I appreciate all the effort that has gone into this but wanted to give another view. We recently ran into this issue when prettier got upgraded and are not so excited about the change:

  • Our company requires all projects to use tabs
  • Our company provides a set of eslint rules to projects, that are not supposed to be overridden
  • Prettier can be used by teams/projects if they want to, but this is not mandatory (as it can be too opinionated for some teams, their loss... 😄)

This change doesn't play well with this setup. We cannot disable the ESLint rule related to mixing spaces and tabs (well we can, but we are asked not to).

So for us, that means we would have to stick to the older version of prettier. 😞

@duailibe
Copy link
Copy Markdown
Collaborator

@jseminck Is/are the rule/rules auto-fixable?

In that case you can use prettier-eslint to fix the stuff after Prettier formats everything

@jseminck
Copy link
Copy Markdown

I am not sure if this is fix-able ( https://eslint.org/docs/rules/no-mixed-spaces-and-tabs ). I will check how that could work. Thanks for the suggestion. 👍

@lydell
Copy link
Copy Markdown
Member

lydell commented Mar 17, 2018

  • Our company provides a set of eslint rules to projects, that are not supposed to be overridden
  • Prettier can be used by teams/projects if they want to

That sounds like a constant uphill battle to me. Why not allow people to use eslint-config-prettier?

@jseminck
Copy link
Copy Markdown

jseminck commented Mar 17, 2018

It hasn't caused any problems until now. We have over 100 repositories and having a shared eslint config helps switching from one project to another a bit easier. 😄

Is there any place where I can follow how this decision was made? I just don't understand why spaces are used when the option is specifically set to use tabs (even it looks prettier).

This is the difference that we noticed:

previous:

function test() {
	<other code>

	return isNotLinkedToAnyOrganisation
		? Promise.resolve({ data: { id: null } })
		: api.get(`/endpoint`, {
				method: 'POST',
				body: JSON.stringify({ name: organisation.org_name }),
			});
}

to current:

function test() {
	<other code>

	return isNotLinkedToAnyOrganisation
		? Promise.resolve({ data: { id: null } })
		: api.get(`/endpoint`, {
				method: 'POST',
				body: JSON.stringify({ name: organisation.org_name }),
		  });
}

Tabs only solution could just simply be so, but maybe this was discussed and not chosen due to some other reasons that I am not aware of:

function test() {
        <other code>

	return isNotLinkedToAnyOrganisation
		? Promise.resolve({ data: { id: null } })
		: api.get(`/endpoint`, {
			method: 'POST',
			body: JSON.stringify({ name: organisation.org_name }),
		});
}

Anyway, I don't want to complain 😄Prettier has already saved me so much time and I have come to love it. If this new approach will be the standard then I will find a way to adjust (eslint-config-prettier seems to be the solution), but just wanted to share my view on this particular topic.

@ikatyang
Copy link
Copy Markdown
Member Author

ikatyang commented Mar 17, 2018

As you can see in your example:

function test() {
	const isNotLinkedToAnyOrganisation = false;
	const api = {};
	const organisation = {};

	return isNotLinkedToAnyOrganisation
		? Promise.resolve({ data: { id: null } })
		: api.get(`/endpoint`, {
				method: 'POST',
				body: JSON.stringify({ name: organisation.org_name }),
-			});
+		  });
}

(I'm not sure if everyone's browser display tabs as 4 char width, so here's the screenshot.)

image

Its alignment looks odd before this PR if your tabs are not displayed as 2 char width, and it's not consistent with space-only:

function test() {
    const isNotLinkedToAnyOrganisation = false;
    const api = {};
    const organisation = {};

    return isNotLinkedToAnyOrganisation
        ? Promise.resolve({ data: { id: null } })
        : api.get(`/endpoint`, {
              method: "POST",
              body: JSON.stringify({ name: organisation.org_name })
          });
}

The only solution I can think of is to add alignment spaces for it, don't know if there's a better one.

(I saw there's a smart-tabs option in ESLint, it should work in this case.)

@wmertens
Copy link
Copy Markdown

wmertens commented Mar 17, 2018 via email

@lock lock Bot added the locked-due-to-inactivity Please open a new issue and fill out the template instead of commenting. label Jan 18, 2019
@lock lock Bot locked as resolved and limited conversation to collaborators Jan 18, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

locked-due-to-inactivity Please open a new issue and fill out the template instead of commenting.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Objects in ternary has extra indent when using --use-tabs

6 participants