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

proposal: html/template: add a formatter #69413

Open
josharian opened this issue Sep 11, 2024 · 16 comments
Open

proposal: html/template: add a formatter #69413

josharian opened this issue Sep 11, 2024 · 16 comments
Labels
Milestone

Comments

@josharian
Copy link
Contributor

Proposal Details

Go was a trailblazer of standardized, no-knobs code formatting. Yet one of the major components of the standard library--one in which I've personally written tens of thousands of lines of code--has no usable formatter. (There's one abandoned open source one that has showstopper bugs. And prettier is oblivious to the Go templating code, with predictable results.)

The surface area would be:

  • a tool, similar to go fmt, that formats an entire file
  • a single API that accepts a string and returns a string and an error, for use e.g. by gopls and linters (e.g. for checking templates stored in literal strings)

I propose that the details of the formatting be left up to the Go team (or whoever steps up to do the implementation), maybe after a preference-gathering round. I care about the details...but I care WAY more that there be a single unified vision behind it and that it be standardized.

@gopherbot gopherbot added this to the Proposal milestone Sep 11, 2024
@ianlancetaylor ianlancetaylor moved this to Incoming in Proposals Sep 11, 2024
@ianlancetaylor
Copy link
Member

CC @robpike

@jimmyfrasche
Copy link
Member

White space can be meaningful in HTML in ways that are not statically determinable. The most obvious way are tags like <pre> but that depends on UA CSS that can be set on any tag (or unset on this one) and that CSS can be set in JS and you may not even know that that tag is being emitted. Changing white space in the HTML can affect styling (:empty) and the information that JS gets when reading the DOM.

Most of the time it doesn't matter and you could get away with some rough heuristics, but you'd need some syntax to flag that a section of a template can't be reformatted for those rare times when it's important and you'd probably want <pre> and friends to implicitly set that flag.

@josharian
Copy link
Contributor Author

@jimmyfrasche I’ll admit to rank ignorance about the details….but somehow or other prettier formats HTML in very opinionated ways and it is very widely used.

@jimmyfrasche
Copy link
Member

It's fine almost all the time. When it's not, you need a way to mark the section that's not safe to format. The alternative is to not format that file at all, which is hard if everyone's editor is set to format on save and so on and no one would think to check if it's okay to format a file because why wouldn't it be?

@hu3bi
Copy link

hu3bi commented Sep 12, 2024

What are the 3rd party options here? Maybe https://github.com/a-h/templ ? I think templ doesn't support html templates from the std lib tough.

@shayn-orca
Copy link

Tossing my 2 cents into the ring. I've turned off formatting on most HTML files in my site because they break some functionality or another (most obvious example is Markdown content inside reveal.js slide tags). It's pretty much impossible to mess around with whitespace in a polyglot document, which HTML documents naturally are, unfortunately.

Sorry Josh :\

@robpike
Copy link
Contributor

robpike commented Sep 12, 2024

I don't actually understand what you're asking for.

@perbu
Copy link

perbu commented Sep 12, 2024

afaic, the proposal is to introduce a tool, likely a part of the Go binary, which would take a file like so:

<h1>{{.PageTitle}}</h1><ul>{{range .Todos}}{{if .Done}}<li class="done">{{.Title}}</li>
{{else}}<li>{{.Title}}</li>{{end}}{{end}}</ul>

and format it so it looks something like this:

<h1>{{.PageTitle}}</h1>
<ul>
    {{range .Todos}}
        {{if .Done}}
            <li class="done">{{.Title}}</li>
        {{else}}
            <li>{{.Title}}</li>
        {{end}}
    {{end}}
</ul>

@perbu
Copy link

perbu commented Sep 12, 2024

Keep in mind that HTML templates can contain both CSS and JS.

@earthboundkid
Copy link
Contributor

There is a plugin for Prettier that I use: https://github.com/NiklasPor/prettier-plugin-go-template

It's not always the best though. It sometimes gets confused.

A challenge for this is gofmt just ignores line length altogether, but an HTML formatter typically needs to decide when to put things on a newline or not, unless you want it to be purely an indentation fixer. There are a lot of design decisions to make.

@earthboundkid
Copy link
Contributor

earthboundkid commented Sep 12, 2024

Most of the time it doesn't matter and you could get away with some rough heuristics, but you'd need some syntax to flag that a section of a template can't be reformatted for those rare times when it's important and you'd probably want <pre> and friends to implicitly set that flag.

With prettier-plugin-go-template, when I need to do something and the formatter keeps screwing it up or I need to do incomplete markup with opening and closing tags in separate templates, I will do something like:

{{- `my  content with weird whitespace or unbalanced HTML here` | safeHTML -}}

@josharian
Copy link
Contributor Author

josharian commented Sep 12, 2024

If doing html (and everything else that goes with it) proves to be infeasible, there is a retrenchment available: format only the stuff completely inside the {{ }}. This also makes it applicable to text/template. And even this would be a help: It would avoid silly conflicts/inconsistency over {{end}} vs {{ end }}, and make more complicated nested expressions readable.

@josharian
Copy link
Contributor Author

josharian commented Sep 12, 2024

One other observation. The highest value (to my mind) is managing indentation. When using {{- and -}}, a formatter is free to insert/delete whitespace without altering the output. When "eat whitespace" is enabled throughout on a chunk of relevant actions, the formatter could manage action indentation. I'd personally go through and switch everything to eat whitespace if it meant I didn't have to constantly manually re-indent everything to keep my code readable.

@qvalentin
Copy link

qvalentin commented Oct 24, 2024

For html templates this project looks already pretty good: djLint.

For yaml templates (e.g. kubernetes helm) this seem like a open challenge to implement.

@josharian
Copy link
Contributor Author

I just ran djLint on my codebase. It appears that it formats Go template actions by destroying all indentation:

{{ range .Choices }}
{{ if .InsertText }}
{{ template "collapsible-code-block" (CodeBlock (Title "Insert") (Code .InsertText)) }}
{{ end }}
{{ if .SnippetText }}
{{ template "collapsible-code-block" (CodeBlock (Title "Snippet") (Code .SnippetText)) }}
{{ end }}
{{ end }}

@ianlancetaylor
Copy link
Member

You said you wanted a single unified vision....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Incoming
Development

No branches or pull requests

10 participants