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

Change HTML/JSX formatting to have one attribute/prop per line #5501

Closed
aboyton opened this issue Nov 19, 2018 · 107 comments · Fixed by #6644
Closed

Change HTML/JSX formatting to have one attribute/prop per line #5501

aboyton opened this issue Nov 19, 2018 · 107 comments · Fixed by #6644
Labels
lang:html Issues affecting HTML (and SVG but not JSX) lang:jsx Issues affecting JSX (not general JS issues) lang:vue Issues affecting Vue locked-due-to-inactivity Please open a new issue and fill out the template instead of commenting. status:needs discussion Issues needing discussion and a decision to be made before action can be taken

Comments

@aboyton
Copy link
Contributor

aboyton commented Nov 19, 2018

Similar to what was done for #3847 I think it best to break some of the discussion from #3101 into a new issue that people can 👍 or 👎.

I'm proposing that for HTML and JSX to have Prettier always have one attribute/prop per line (and thus not respect the developer's original preference). Code would then be formatted as shown below. This is in contrast to the current behaviour where we fit as much as possible with the print-width.

Expected behavior:

<MyComponent lorem="1"/>

<MyComponent
  lorem="1"
  ipsum="2"
/>

<MyComponent
  lorem="1"
  ipsum="2"
  dolor="3"
/>

This suggestion for one attribute/prop per line was proposed several times in the discussion of #3101 but I think it is clearer if this is pulled into it's own proposal. The original proposal in #3101 is that Prettier would add an option to preserve original formatting which, while I agree with the author with the style that they want, I don't think a) an option, nor b) preserving original formatting follows the aims for Prettier (see also #2068). Instead I think the aims of #3101 are better served by this proposal to ignore the print-width and always place attributes/props on new lines (assuming that there is more than one).

This style appears to be the most common formatting for Angular, Vue and React from what I can tell. It style appears to be the style enforced by the rules:

@lydell lydell added status:needs discussion Issues needing discussion and a decision to be made before action can be taken lang:jsx Issues affecting JSX (not general JS issues) lang:html Issues affecting HTML (and SVG but not JSX) labels Nov 19, 2018
@ghost
Copy link

ghost commented Nov 26, 2018

While I agree that I'd like to see this being default behavior, I would suggest the following steps:

  • introduce new config option (singleAttributePerLine: true ?)
  • make default of that option to have attributes on single line (for backwards compatibility)
  • now we can have it 🎉
  • open discussion about changing the default behavior
  • depending on the outcome:
    • change default behavior of the option
    • deprecate option

@leipert
Copy link

leipert commented Nov 26, 2018

@lydell This probably also should have lang:vue as a label 😄

@lydell lydell added the lang:vue Issues affecting Vue label Nov 26, 2018
@j-f1

This comment has been minimized.

@marosivanco
Copy link

It looks neat only on simple examples. Consider this fragment:

<h2 slot="header">What is your income?</h2> 
<template v-if="employmentType === 'Entrepreneur'">
    <ui-currency v-model="income.business.incomePrevYear" :minInclusive="0" required key="incomePrevYear">Last year income</ui-currency>
    <ui-currency v-model="income.business.income" :minInclusive="0" required key="income">Current income</ui-currency>
    <ui-date v-model="income.business.incomeToDate" :minDate="minDate" max-date="today" required>Income to date</ui-date>
    <ui-currency v-model="income.business.taxableIncome" :minInclusive="0" required key="taxableIncome">Last year tax assessment base</ui-currency>
    <ui-currency v-model="income.business.incomeTax" :minInclusive="0" required key="incomeTax">Tax value</ui-currency>
</template>
<template v-else>
    <label class="field text salary">
        <span class="label">Net income for last 3 months *</span>
        <span>
            <ui-currency v-model="income.personal.salaries[0].value" :minInclusive="0" required key="fMonth" placeholder="1. month*"></ui-currency>
            <ui-currency v-model="income.personal.salaries[1].value" :minInclusive="0" required key="sMonth" placeholder="2. month *"></ui-currency>
            <ui-currency v-model="income.personal.salaries[2].value" :minInclusive="0" required key="tMonth" placeholder="3. month *"></ui-currency>
        </span>
    </label>
    <ui-currency v-model="income.personal.otherIncome" :minInclusive="0" key="otherIncome">Sum of other incomes</ui-currency>
</template>
<ui-select v-if="contract.calculatedProduct.class === 'ConsumerCredit'" v-model="income.personal.nonCashIncome" :options="decisions" :optionName="opt => opt ? 'Yes' : 'No'" required>Income sent to bank account</ui-select>

The structure of the template is obvious, so is the data-binding. The other, less important information (validation, l10n,...) comes next.
If you "format" the code using current rules, you get... well, something similar to a punch tape.

The point I am trying to make is that the vertical space matters. Wasting it (too much) has negative impact on code manageability.

I would suggest to stick with object-literal-like formatting, which has two forms, either:

const a = { prop1, prop1 };

or

const a = {
              prop1,
              prop2,
            };

From the two, the first one is currently missing (in HTML formatting).

@ghost
Copy link

ghost commented Nov 27, 2018

The point I am trying to make is that the vertical space matters. Wasting it (too much) has negative impact on code manageability.

On the other hand, many diff tools are line-based. So by putting each attribute on it's own line, we reduce the number of conflicts a team has to solve.

From the example above consider

-     <ui-currency v-model="income.business.incomePrevYear" :minInclusive="0" required key="incomePrevYear">Last year income</ui-currency>
+     <ui-currency v-model="income.business.incomePrevYear" :minInclusive="1" required key="incomePrevYear">Last year income</ui-currency>

vs.

    <ui-currency
      v-model="income.business.incomePrevYear"
-     :minInclusive="0"
+     :minInclusive="1"
      required
      key="incomePrevYear"
    >Last year income</ui-currency>

@marosivanco
Copy link

Sure, if the division of labor has a form that results in many conflicts, then yes, it can help. Or if the formatting does not result in a "punch tape".
I am not really against it. I am just saying, that the first formatting option is also valid and both options should be supported (as in the case of JS object literal).

@bengry
Copy link

bengry commented Nov 28, 2018

I like the idea in concept, and to not have an option for this, however - I agree with @marosivanco's idea above of keeping it more like how objects are formatted today, and add what @scrimothy wrote here:

@JoshMcCullough I think treat the attributes as an object literal should be a good compromise. If the input element is with the first attribute in the same line, then it should colapse to one line if it fits. (This is how objects literals behave in 1.15). If is in another line, it doesn't matter how many attributes it have, it should expand.

  • Input:
<my-table [header]="header"
          [items]="items"
          (onScroll)="onScroll($event)">
</my-table>
  • Output:
<my-table [header]="header" [items]="items" (onScroll)="onScroll($event)">
</my-table>
  • Input:
<MyComponent
  foo="John" bar="Smith"
/>
  • Output:
<MyComponent
  foo="John"
  bar="Smith"
/>

IMO this could be a good approach in something that have some many opinions around it. There are many different ways to format element attributes in HTML, but as I said, this seems like a good compromise.

It seems appropriate to focus on breaking HTML-like elements to object literal format after exceeding a given line length (which should just correspond to the print-width attribute rather than adding another length flag specific to this rule). Otherwise, you may have a very long single attribute that would do well on the next line, or 2 or 3 very short attributes that would work perfectly well all on one line. Ultimately, the long line length is the biggest hindrance to readability.

Similarly, sometimes the cause of a long line has more to do with the length of your custom component's name more so than the attributes' key/value pairs.

Input:

<my-app-prefix-comp-a attr1="foo" attr2="bar" />

<my-app-prefix-comp-b attribute-one="methodOneReturnValue()" attribute-two="methodTwoReturnValue()" />

<my-app-prefix-with-a-very-long-custom-component-name attr1="foo" attr2="bar" />

Output:

<my-app-prefix-comp-a attr1="foo" attr2="bar" />

<my-app-prefix-comp-b
    attribute-one="methodOneReturnValue()"
    attribute-two="methodTwoReturnValue()"
/>

<my-app-prefix-with-a-very-long-custom-component-name
    attr1="foo"
    attr2="bar"
/>

I think this should satisfy, or at least be acceptable, for most people - we both have no new option(s) yet still format the input, allowing both putting as many attributes/props as fit in the same line, or break each one to its own line (if the first attribute/prop is put in the line below the open tag).

@mbifulco
Copy link

Any support for something like this would be great. At the moment, prettier only seems to collapse jsx attributes to be on a single line - of all of the proposed options, (for me) that's the least desirable.

@helios1138
Copy link

This would be very good to have

@AlexSwtlsk
Copy link

Still no solution at this day ?

@webberwang
Copy link

webberwang commented Apr 25, 2019

Having that option would at least allow people to opt in. Is this something too time-consuming to implement?

@lydell
Copy link
Member

lydell commented Apr 26, 2019

@webberwang See our (slightly outdated) option philosophy.

@mbifulco
Copy link

I will say that since posting in this issue, I have adopted prettier across my projects, and I am absolutely never going back. The productivity increase far outweighs my old preferences for syntax formatting. 🎉

@mittalyashu

This comment has been minimized.

@lydell

This comment has been minimized.

@mittalyashu

This comment has been minimized.

@lydell

This comment has been minimized.

@evankennedy
Copy link

@mbifulco While I agree buying into the Prettier opinions of syntactic styling allows for developers to focus on less trivial matters, I'd argue that this specific issue is more important than a strictly syntactic issue such as using tabs vs spaces (though I'm still very appreciative that Prettier allows tabs, even though that issue is one I would have "bought in" to).

Here is why I see this as more than just "preferred syntax":

  • Improved code scan-ability: we can search for the desired attribute in a list, not a grid
  • Consolidated diffs: only the attribute you changed is in the diff, could reduce merge conflicts
  • Compatibility with major frameworks: looking at Vue here, their docs and eslint plugin prefer this format

Disabling recommendations of major frameworks is one approach, but if Prettier defines one standard and doesn't adapt as the leading edge of development adapts, it will ultimately be left behind and replaced with something that does.

That's my opinion, and of course reasonable people can disagree 🙂

@alexander-akait
Copy link
Member

/cc @vjeux Want you feedback. It is really hard to read when we have multiple properties in one lines

@lydell

This comment has been minimized.

@scherii
Copy link

scherii commented Apr 19, 2021

Yes, I have read it and still think it is an elegant solution to the problem.

At this point I would like to note that the last sentence of the yellow box points to the real problem. When I shorten or remove an attribute, the whole element collapses back to one line.

This useless change might even get included in a commit, which is exactly the kind of situation Prettier was created to prevent.

A single line of code with multiple attributes is deleted in the git diff and replaced with multiple lines of code (the code example is not ideal, but it is sufficient).

Edit: @lydell I am aware that this argument has already been discussed in detail. What I would like to say is:
It is a better solution than no solution. As in the original case of Object Literals.

I guess there is no need for more reasons and there is no discussion. There will never be an option because of the philosophy. In my opinion, the formatting of multiline attributes in HTML (etc.) would have to follow the same principle as for Object Literals and others. Consistency is key. If not, as @sanmai-NL already asked in December - quite rightly - it would be time to remove the label and close this Issue.

@peacechen
Copy link

It's dismaying that the prettier team has dug their heels in on this based on philosophical grounds. The fact of the matter is that formatting rules for Javascript don't universally apply to JSX and vice versa. Attempts to do so result in undesirable output. Single line JSX props are preferred by the industry for 2+ props.

ESLint does a fine job of linting and formatting. It takes some more configuration initially, but once that's done it's done. Switch over and save yourself the frustrations of talking to a brick wall.

@sam-cma
Copy link

sam-cma commented Apr 19, 2021

Shouldn't a good formatting tool provide options rather than force its own standard?
I'm moving on to Eslint.

Good luck with enforcing your "standards"

@scherii
Copy link

scherii commented Apr 22, 2021

@peacechen I think your "dug their heels in" captures the essence of it. Perhaps it is time to reconsider that logic based on the reasoning in this thread (and the similar one for #2550).

No better solution to the original multi-line problem has been found for some time. In my opinion, now would be the right moment to consequently apply the workaround to other areas as well.

@lydell Would you kindly get in touch with the rest of the Prettier team to advance this issue? Thank you very much. 🙂

@pranav-js
Copy link

year 2021. somebody fix this please for HTML 🥲

@mixn
Copy link

mixn commented May 28, 2021

1520596752_FeelsBadMan

@fdutey
Copy link

fdutey commented Aug 1, 2021

Being so close minded just amazes me

@samgermain
Copy link

samgermain commented Aug 7, 2021

I don't want to use prettier if this is the only option. I want to be able to have

"paths": {
            "components": ["./src/components"],
            "components/*": ["./src/components/*"],
            "data": ["./src/data"],
            "data/*": ["./src/data/*"],
            "images": ["./src/assets/images"],
            "images/*": ["./src/assets/images/*"],
            "pdf": ["./src/assets/pdf"],
            "pdf/*": ["./src/assets/pdf/*"],
            "styles": ["./src/styles"],
            "styles/*": ["./src/styles/*"],
            "svg": ["./src/assets/images/svg"],
            "svg/*": ["./src/assets/images/svg/*"],
            "util": ["./src/util"],
            "util/*": ["./src/util/*"],
            "videos": ["./src/assets/videos"],
            "videos/*": ["./src/assets/videos/*"],
        },

instead of

"paths": {
            "components": [
                "./src/components"
            ],
            "components/*": [
                "./src/components/*"
            ],
            "data": [
                "./src/data"
            ],
            "data/*": [
                "./src/data/*"
            ],
            "images": [
                "./src/assets/images"
            ],
            "images/*": [
                "./src/assets/images/*"
            ],
            "pdf": [
                "./src/assets/pdf"
            ],
            "pdf/*": [
                "./src/assets/pdf/*"
            ],
            "styles": [
                "./src/styles"
            ],
            "styles/*": [
                "./src/styles/*"
            ],
            "svg": [
                "./src/assets/images/svg"
            ],
            "svg/*": [
                "./src/assets/images/svg/*"
            ],
            "util": [
                "./src/util"
            ],
            "util/*": [
                "./src/util/*"
            ],
            "videos": [
                "./src/assets/videos"
            ],
            "videos/*": [
                "./src/assets/videos/*"
            ],
        },

The first one is obviously way better

@sardapv
Copy link

sardapv commented Aug 7, 2021

I don't want to use prettier if this is the only option. I want to be able to have

"paths": {
            "components": ["./src/components"],
            "components/*": ["./src/components/*"],
            "data": ["./src/data"],
            "data/*": ["./src/data/*"],
            "images": ["./src/assets/images"],
            "images/*": ["./src/assets/images/*"],
            "pdf": ["./src/assets/pdf"],
            "pdf/*": ["./src/assets/pdf/*"],
            "styles": ["./src/styles"],
            "styles/*": ["./src/styles/*"],
            "svg": ["./src/assets/images/svg"],
            "svg/*": ["./src/assets/images/svg/*"],
            "util": ["./src/util"],
            "util/*": ["./src/util/*"],
            "videos": ["./src/assets/videos"],
            "videos/*": ["./src/assets/videos/*"],
        },

instead of

"paths": {
            "components": [
                "./src/components"
            ],
            "components/*": [
                "./src/components/*"
            ],
            "data": [
                "./src/data"
            ],
            "data/*": [
                "./src/data/*"
            ],
            "images": [
                "./src/assets/images"
            ],
            "images/*": [
                "./src/assets/images/*"
            ],
            "pdf": [
                "./src/assets/pdf"
            ],
            "pdf/*": [
                "./src/assets/pdf/*"
            ],
            "styles": [
                "./src/styles"
            ],
            "styles/*": [
                "./src/styles/*"
            ],
            "svg": [
                "./src/assets/images/svg"
            ],
            "svg/*": [
                "./src/assets/images/svg/*"
            ],
            "util": [
                "./src/util"
            ],
            "util/*": [
                "./src/util/*"
            ],
            "videos": [
                "./src/assets/videos"
            ],
            "videos/*": [
                "./src/assets/videos/*"
            ],
        },

The first one is obviously way better

Nah I would drop review comment 😴. 2nd is easy to read.

@samgermain
Copy link

I don't want to use prettier if this is the only option. I want to be able to have

"paths": {
            "components": ["./src/components"],
            "components/*": ["./src/components/*"],
            "data": ["./src/data"],
            "data/*": ["./src/data/*"],
            "images": ["./src/assets/images"],
            "images/*": ["./src/assets/images/*"],
            "pdf": ["./src/assets/pdf"],
            "pdf/*": ["./src/assets/pdf/*"],
            "styles": ["./src/styles"],
            "styles/*": ["./src/styles/*"],
            "svg": ["./src/assets/images/svg"],
            "svg/*": ["./src/assets/images/svg/*"],
            "util": ["./src/util"],
            "util/*": ["./src/util/*"],
            "videos": ["./src/assets/videos"],
            "videos/*": ["./src/assets/videos/*"],
        },

instead of

"paths": {
            "components": [
                "./src/components"
            ],
            "components/*": [
                "./src/components/*"
            ],
            "data": [
                "./src/data"
            ],
            "data/*": [
                "./src/data/*"
            ],
            "images": [
                "./src/assets/images"
            ],
            "images/*": [
                "./src/assets/images/*"
            ],
            "pdf": [
                "./src/assets/pdf"
            ],
            "pdf/*": [
                "./src/assets/pdf/*"
            ],
            "styles": [
                "./src/styles"
            ],
            "styles/*": [
                "./src/styles/*"
            ],
            "svg": [
                "./src/assets/images/svg"
            ],
            "svg/*": [
                "./src/assets/images/svg/*"
            ],
            "util": [
                "./src/util"
            ],
            "util/*": [
                "./src/util/*"
            ],
            "videos": [
                "./src/assets/videos"
            ],
            "videos/*": [
                "./src/assets/videos/*"
            ],
        },

The first one is obviously way better

Nah I would drop review comment 😴. 2nd is easy to read.

Is that a joke? Also what does drop review comment mean

@sardapv
Copy link

sardapv commented Aug 7, 2021

@samgermain haha just kidding man. Everyone has different choices 😀. I would prefer to keep precommit hooks to prettify using common config for same settings acrosss team workspace. Even if someone doesn't follow, atleast code in repo would be in same config for all

@alexander-akait
Copy link
Member

alexander-akait commented Dec 2, 2021

Merged #6644 and I hope this will make life better for many

@pranav-js
Copy link

Merged #6644 and I hope this will make life better for many

Ailabouuu ❤️😘

@aradalvand
Copy link

aradalvand commented Dec 3, 2021

@alexander-akait Sorry if this is an awkward question but when will this option make it to the Prettier VS Code extension?

@glen-84
Copy link

glen-84 commented Mar 6, 2022

@AradAral I don't think that this change has been released yet.

@krokofant
Copy link

Now 3 months since last release, is there a todo before a new version can be released? 🤔

@jdthorpe
Copy link

jdthorpe commented Mar 9, 2022

For VSCode users who find this single-line-per-prop rule as obnoxious as I do, create a .vscode/settings.json file at the root of your project with these contents:

{
    "[javascriptreact]": {
        "editor.defaultFormatter": "vscode.typescript-language-features",
        "editor.tabSize": 2 // other options as needed
    },
    "[typescriptreact]": {
        "editor.defaultFormatter": "vscode.typescript-language-features",
        "editor.tabSize": 2
    }
}

to disable prettier on you JSX files, or add the same content to your global settings.json file

@hermesjpappas
Copy link

Hey Guys I've been through a lot of pain until I found a way for the html files specifically. I like the simplicity of prettier and I use it for my TS and JS files but for the html I use the default formatter that ships with VSC. The default formatter is based on js-beautify. Which seems to have that ability to wrap each of the attributes on that line. Here you can read more
(https://code.visualstudio.com/Docs/languages/html#_formatting
What I did in my case was to set in my settings.json
"html.format.wrapAttributes": "force-aligned"
That is doing exactly what I wanted in my case. You can check the page and see what other options the default formatter have and you can configure it for your cases but I think it will satisfy most of your needs regarding the formatting of the html files

For me "html.format.wrapAttributes": "force-expand-multiline" worked well.

Thank you so much for this, this has solved it for the moment 👍

@github-actions github-actions bot added the locked-due-to-inactivity Please open a new issue and fill out the template instead of commenting. label Nov 27, 2023
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Nov 27, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
lang:html Issues affecting HTML (and SVG but not JSX) lang:jsx Issues affecting JSX (not general JS issues) lang:vue Issues affecting Vue locked-due-to-inactivity Please open a new issue and fill out the template instead of commenting. status:needs discussion Issues needing discussion and a decision to be made before action can be taken
Projects
None yet
Development

Successfully merging a pull request may close this issue.