Skip to content

Commit 581f6d3

Browse files
committed
fix attrTokenRe splitting attributes with template actions containing quotes
The attrTokenRe regex used [^"]* for double-quoted attribute values, which stopped at the first quote inside a template action. For attributes like aria-sort="{{if eq .SortBy "check_in"}}...", the regex matched only up to the inner quote, splitting the attribute and corrupting formatted output. Updated the regex to match {{...}} template actions atomically before individual characters, so inner quotes within actions are consumed as part of the action. Also replaced the allOverflow wrapping bailout with a simpler len(attrs)<=1 check. The previous logic incorrectly abandoned wrapping when a long template-containing attribute was the last attr on a continuation line, even though wrapping still reduced the first line's width.
1 parent 2db01e4 commit 581f6d3

4 files changed

Lines changed: 45 additions & 10 deletions

File tree

.beans/.sync.json

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@
2525
"synced_at": "2026-02-02T23:55:16.331753Z"
2626
}
2727
},
28+
"golsp-hyo6": {
29+
"clickup": {
30+
"task_id": "868hb9ny4",
31+
"synced_at": "2026-02-03T00:09:58.074886Z"
32+
}
33+
},
2834
"golsp-ijrh": {
2935
"clickup": {
3036
"task_id": "868hb0hr0",
@@ -72,6 +78,12 @@
7278
"task_id": "868hb9fwx",
7379
"synced_at": "2026-02-02T23:55:16.39934Z"
7480
}
81+
},
82+
"golsp-whzj": {
83+
"clickup": {
84+
"task_id": "868hb9ny2",
85+
"synced_at": "2026-02-03T00:09:57.984258Z"
86+
}
7587
}
7688
}
7789
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
# golsp-9w5l
3+
title: Fix attrTokenRe splitting attributes with template actions containing quotes
4+
status: completed
5+
type: bug
6+
priority: normal
7+
created_at: 2026-02-03T00:56:48Z
8+
updated_at: 2026-02-03T01:01:19Z
9+
---
10+
11+
The attrTokenRe regex uses [^"]* for double-quoted attribute values, which breaks on attributes like aria-sort="{{if eq .SortBy "check_in"}}ascending{{else}}none{{end}}". The regex matches up to the first inner quote, splitting the attribute into garbage tokens and corrupting the formatted output.

internal/template/format.go

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ var (
3636
// attrTokenRe matches individual attribute chunks: name="value", name='value',
3737
// name, or template actions {{...}}.
3838
attrTokenRe = regexp.MustCompile(
39-
`(\{\{.*?\}\}(?:[^\s<>]*\{\{.*?\}\})*[^\s<>]*|\S+?="[^"]*"|\S+?='[^']*'|[^\s>]+)`,
39+
`(\{\{.*?\}\}(?:[^\s<>]*\{\{.*?\}\})*[^\s<>]*|\S+?="(?:\{\{.*?\}\}|[^"])*"|\S+?='(?:\{\{.*?\}\}|[^'])*'|[^\s>]+)`,
4040
)
4141

4242
// tmplBlockRe matches template block-opening keywords.
@@ -325,15 +325,9 @@ func wrapAttributes(
325325
lines = append(lines, currentLine+closer+afterClose)
326326
}
327327

328-
// If every continuation line still exceeds printWidth, wrapping didn't help
329-
allOverflow := true
330-
for _, line := range lines[1:] {
331-
if lineWidth(line, opts.TabSize) <= opts.PrintWidth {
332-
allOverflow = false
333-
break
334-
}
335-
}
336-
if allOverflow {
328+
// With only one attribute, wrapping just moves it to the next line without
329+
// reducing the overall width — skip wrapping in that case.
330+
if len(attrs) <= 1 {
337331
return nil, false
338332
}
339333

internal/template/format_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -739,6 +739,24 @@ title="Expand">`,
739739
</section>
740740
</dialog>
741741
{{end}}`,
742+
},
743+
{
744+
name: "attribute with template action containing inner quotes",
745+
input: `<th scope="col" class="booked-on" data-sort-field="booked_on" aria-sort="{{if eq .SortBy "booked_on"}}ascending{{else if eq .SortBy "-booked_on"}}descending{{else}}none{{end}}">
746+
Booked<br/>On
747+
<span class="sort-icon"></span>
748+
</th>`,
749+
opts: FormatOptions{
750+
TabSize: 3,
751+
InsertSpaces: true,
752+
PrintWidth: 100,
753+
AttrWrapMode: "overflow",
754+
},
755+
expected: `<th scope="col" class="booked-on" data-sort-field="booked_on"
756+
aria-sort="{{if eq .SortBy "booked_on"}}ascending{{else if eq .SortBy "-booked_on"}}descending{{else}}none{{end}}">
757+
Booked<br/>On
758+
<span class="sort-icon"></span>
759+
</th>`,
742760
},
743761
{
744762
name: "default opts no wrapping",

0 commit comments

Comments
 (0)