feat(fmt): go-fmt-style recursive formatting and canonical blank-line layout
Assisted-by: Qwen 3.8 Max Preview
This commit is contained in:
+82
-13
@@ -27,14 +27,6 @@ func Source(path, src string) string {
|
||||
mnemLen int
|
||||
funcID int
|
||||
}
|
||||
const (
|
||||
kBlank = iota
|
||||
kComment
|
||||
kPreproc
|
||||
kDirective
|
||||
kLabel
|
||||
kInstr
|
||||
)
|
||||
|
||||
infos := make([]info, len(lines))
|
||||
funcID := -1
|
||||
@@ -70,8 +62,8 @@ func Source(path, src string) string {
|
||||
infos[i] = inf
|
||||
}
|
||||
|
||||
// Second pass: render.
|
||||
var b strings.Builder
|
||||
// Second pass: render each line.
|
||||
outs := make([]outLine, 0, len(lines))
|
||||
inBody := false
|
||||
for i, line := range lines {
|
||||
inf := infos[i]
|
||||
@@ -106,10 +98,87 @@ func Source(path, src string) string {
|
||||
inBody = false
|
||||
}
|
||||
}
|
||||
b.WriteString(strings.TrimRight(out, " \t"))
|
||||
b.WriteByte('\n')
|
||||
outs = append(outs, outLine{kind: inf.kind, text: strings.TrimRight(out, " \t")})
|
||||
}
|
||||
return b.String()
|
||||
return normalizeSpacing(outs)
|
||||
}
|
||||
|
||||
// Line classification, shared by the formatting passes.
|
||||
const (
|
||||
kBlank = iota
|
||||
kComment
|
||||
kPreproc
|
||||
kDirective
|
||||
kLabel
|
||||
kInstr
|
||||
)
|
||||
|
||||
// outLine is one rendered line together with its classification.
|
||||
type outLine struct {
|
||||
kind int
|
||||
text string
|
||||
}
|
||||
|
||||
// normalizeSpacing enforces the canonical blank-line layout: runs of blank
|
||||
// lines collapse to one, and a new block — a label, or a TEXT or GLOBL
|
||||
// directive — is preceded by exactly one blank line. Comments immediately
|
||||
// above a block belong to it, so the blank line is inserted before them. No
|
||||
// blank line is forced at the top of the file, right after a TEXT (the
|
||||
// function's first label), or between stacked labels that share an address.
|
||||
func normalizeSpacing(outs []outLine) string {
|
||||
blockStart := func(ol outLine) bool {
|
||||
switch ol.kind {
|
||||
case kLabel:
|
||||
return true
|
||||
case kDirective:
|
||||
// TEXT and GLOBL open a block; DATA continues a GLOBL block.
|
||||
return strings.HasPrefix(ol.text, "TEXT") || strings.HasPrefix(ol.text, "GLOBL")
|
||||
}
|
||||
return false
|
||||
}
|
||||
insert := make([]bool, len(outs))
|
||||
for i, ol := range outs {
|
||||
if !blockStart(ol) {
|
||||
continue
|
||||
}
|
||||
j := i
|
||||
for j > 0 && outs[j-1].kind == kComment {
|
||||
j--
|
||||
}
|
||||
if j == 0 {
|
||||
continue // top of file
|
||||
}
|
||||
switch prev := outs[j-1]; {
|
||||
case prev.kind == kBlank, prev.kind == kLabel:
|
||||
continue // already separated, or stacked labels
|
||||
case prev.kind == kDirective && strings.HasPrefix(prev.text, "TEXT"):
|
||||
continue // the function's first label
|
||||
}
|
||||
insert[j] = true
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
prevBlank := true // also suppresses leading blanks
|
||||
for i, ol := range outs {
|
||||
if insert[i] && !prevBlank {
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
if ol.kind == kBlank {
|
||||
if !prevBlank {
|
||||
b.WriteByte('\n')
|
||||
}
|
||||
prevBlank = true
|
||||
continue
|
||||
}
|
||||
b.WriteString(ol.text)
|
||||
b.WriteByte('\n')
|
||||
prevBlank = false
|
||||
}
|
||||
out := strings.TrimRight(b.String(), "\n")
|
||||
if out == "" {
|
||||
return ""
|
||||
}
|
||||
return out + "\n"
|
||||
}
|
||||
|
||||
// renderInstr renders an instruction line: a tab, the mnemonic padded to the
|
||||
|
||||
Reference in New Issue
Block a user