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

templ fmt does not indent style tag properly #1010

Open
momrak opened this issue Dec 6, 2024 · 4 comments
Open

templ fmt does not indent style tag properly #1010

momrak opened this issue Dec 6, 2024 · 4 comments
Labels
bug Something isn't working good first issue Good for newcomers

Comments

@momrak
Copy link

momrak commented Dec 6, 2024

Say I have the following code
CleanShot 2024-12-06 at 14 34 01

After running templ fmt . it will look like this.

CleanShot 2024-12-06 at 14 38 16

So the divs and such are are indented correctly, but the closing style tag with content is not.

Please let me know if more info is needed.
templ info output
CleanShot 2024-12-06 at 14 40 20

@a-h a-h added bug Something isn't working good first issue Good for newcomers labels Jan 6, 2025
@RyanTalbot
Copy link

Happy to take this on.

@RyanTalbot
Copy link

So I've been trying to get a little more familiar with this issue. I've mainly been focusing my efforts in generator.go around indent levels. From the code example provided, it looks like the style tag is not indenting child nodes as we expect, and its closing tag.

Knowing that, I think the issue is inside either writeTemplBuffer, writeTemplate, or writeNode.

I will keep poking around for now but could probably use some confirmation I'm at least looking in the right area.

And just so we're on the same page, this is what the expected result should be, right?

templ leftRight(left templ.Component, right templ.Component) {
    <style>
        body {
            background: black;
        }
    </style>
    <div class="flex flex-col items-center justify-center h-screen">
        <div></div>
        <div class="flex flex-row">
            @left
            @right
        </div> 
        <div>
            Se priser
        </div>
    </div>
}

@therealparmesh
Copy link
Contributor

I think this does the same thing with <script> tags - mainly the closing </script> tags.

@a-h
Copy link
Owner

a-h commented Jan 22, 2025

So, you've probably figured out that templ fmt formats templ files by parsing a template file into an object model, then writing it back out again.

You can see that in action here:

func format(write writer, read reader, writeIfUnchanged bool) (err error, fileChanged bool) {
fileName, src, err := read()
if err != nil {
return err, false
}
t, err := parser.ParseString(src)
if err != nil {
return err, false
}
t.Filepath = fileName
t, err = imports.Process(t)
if err != nil {
return err, false
}
w := new(bytes.Buffer)
if err = t.Write(w); err != nil {
return fmt.Errorf("formatting error: %w", err), false
}
fileChanged = (src != w.String())
if !writeIfUnchanged && !fileChanged {
return nil, fileChanged
}
return write(fileName, w.String()), fileChanged
}

Note that the TemplateFile.Write method writes the object model out:

templ/parser/v2/types.go

Lines 128 to 150 in 8501201

func (tf TemplateFile) Write(w io.Writer) error {
for _, n := range tf.Header {
if err := n.Write(w, 0); err != nil {
return err
}
}
var indent int
if err := tf.Package.Write(w, indent); err != nil {
return err
}
if _, err := io.WriteString(w, "\n\n"); err != nil {
return err
}
for i := 0; i < len(tf.Nodes); i++ {
if err := tf.Nodes[i].Write(w, indent); err != nil {
return err
}
if _, err := io.WriteString(w, getNodeWhitespace(tf.Nodes, i)); err != nil {
return err
}
}
return nil
}

So you can see that each Node gets written out in turn.

The script and style elements are parsed into the following types:

templ/parser/v2/types.go

Lines 669 to 703 in 8501201

type RawElement struct {
Name string
Attributes []Attribute
Contents string
}
func (e RawElement) IsNode() bool { return true }
func (e RawElement) Write(w io.Writer, indent int) error {
// Start.
if err := writeIndent(w, indent, "<", e.Name); err != nil {
return err
}
for i := 0; i < len(e.Attributes); i++ {
if _, err := w.Write([]byte(" ")); err != nil {
return err
}
a := e.Attributes[i]
// Don't indent the attributes, only the conditional attributes get indented.
if err := a.Write(w, 0); err != nil {
return err
}
}
if _, err := w.Write([]byte(">")); err != nil {
return err
}
// Contents.
if _, err := w.Write([]byte(e.Contents)); err != nil {
return err
}
// Close.
if _, err := w.Write([]byte("</" + e.Name + ">")); err != nil {
return err
}
return nil
}

By this parsing code: https://github.com/a-h/templ/blob/850120161a0434ee73b92810f02e4def8a70c850/parser/v2/raw.go

I can see that the style and script tags don't populate out a TrailingSpace, like the other elements do:

e.TrailingSpace, err = NewTrailingSpace(ws)

I think ths issue is probably:

  • Not parsing trailing whitespace into the object model, so it can't be retained properly.
  • Not writing out appropriate indentation in the Write method to ensure that the </script> / </style> element is properly indented.

Note that there's some formatting tests alonside the templ fmt command, so that's a good place to add test cases.

Hope that helps you track it down!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

4 participants