feat(format): select_wrap and join_wrap with when_long via Doc-IR

This commit is contained in:
Hein
2026-10-06 14:51:24 +02:00
parent ab9fe8893b
commit c51e8e2da4
6 changed files with 110 additions and 2 deletions
+37 -1
View File
@@ -206,6 +206,27 @@ func dmlJoinClause(kwText string, body []cst.Tok, st config.Style) string {
if text != "" {
line += " " + text
}
if onIdx := dmlKeywordIdx(body, 0, "on"); onIdx >= 0 && st.JoinWrap != config.WrapNever {
nl := st.Newline
var b strings.Builder
b.WriteString(kwText)
if head := dmlInline(body[:onIdx], st); head != "" {
b.WriteString(" " + head)
}
for i, cond := range dmlSplitAndOr(body[onIdx+1:]) {
b.WriteString(nl + st.Indent)
if i == 0 {
b.WriteString(caseText(body[onIdx].Tok, st) + " ")
}
b.WriteString(strings.ReplaceAll(dmlInline(cond, st), nl, nl+st.Indent))
}
broken := b.String()
if st.JoinWrap == config.WrapAlways || strings.Contains(line, nl) {
line = broken
} else {
line = Render(Group(IfBreak(Text(broken), Text(line))), st.LineWidth, st.Indent, nl)
}
}
if !st.IndentJoin {
return line
}
@@ -720,16 +741,31 @@ func dmlColListSelect(kwText string, items [][]cst.Tok, st config.Style) string
}
texts := make([]string, len(items))
multiline := false
for i, item := range items {
texts[i] = dmlInline(item, st)
if strings.Contains(texts[i], st.Newline) {
multiline = true
}
}
flat := kwText + " " + strings.Join(texts, ", ")
// align_columns / select_align_as: pad expressions so AS and aliases align.
if (st.AlignColumns || st.SelectAlignAs) && len(texts) > 1 {
texts = alignSelectItems(texts, st)
}
return dmlCommaList(kwText, texts, st)
broken := dmlCommaList(kwText, texts, st)
if multiline {
return broken // an embedded subquery / wrapped CASE can't sit on one line
}
switch st.SelectWrap {
case config.WrapNever:
return flat
case config.WrapWhenLong:
return Render(Group(IfBreak(Text(broken), Text(flat))), st.LineWidth, st.Indent, st.Newline)
}
return broken
}
// dmlColListSet formats an UPDATE SET column list with optional set_align_equal.
+47
View File
@@ -647,3 +647,50 @@ func TestDMLCaseCollapseUsesLineWidth(t *testing.T) {
t.Errorf("exceeds 30, should wrap:\n%s", got)
}
}
func TestSelectWrap(t *testing.T) {
src := "select a, b, c from t"
st := config.Default()
if got := File(parser.Parse(src), st); !strings.Contains(got, "\n ,b") {
t.Errorf("default select_wrap=always should list one per line:\n%s", got)
}
st.SelectWrap = config.WrapNever
if got := File(parser.Parse(src), st); !strings.Contains(got, "SELECT a, b, c") {
t.Errorf("never:\n%s", got)
}
st.SelectWrap = config.WrapWhenLong
st.LineWidth = 120
if got := File(parser.Parse(src), st); !strings.Contains(got, "SELECT a, b, c") {
t.Errorf("when_long, short:\n%s", got)
}
st.LineWidth = 8
if got := File(parser.Parse(src), st); !strings.Contains(got, "\n ,b") {
t.Errorf("when_long, long:\n%s", got)
}
}
func TestJoinWrap(t *testing.T) {
src := "select a from t join u on t.id = u.id and t.x = u.x"
st := config.Default()
base := File(parser.Parse(src), st)
if !strings.Contains(base, "JOIN u ON t.id = u.id AND t.x = u.x") {
t.Fatalf("default join_wrap=never should stay inline:\n%s", base)
}
st.JoinWrap = config.WrapAlways
got := File(parser.Parse(src), st)
if !strings.Contains(got, "JOIN u\n ON t.id = u.id\n AND t.x = u.x") {
t.Errorf("always:\n%s", got)
}
if twice := File(parser.Parse(got), st); twice != got {
t.Errorf("not idempotent:\n%s\n---\n%s", got, twice)
}
st.JoinWrap = config.WrapWhenLong
st.LineWidth = 120
if File(parser.Parse(src), st) != base {
t.Errorf("when_long short should equal inline")
}
st.LineWidth = 20
if File(parser.Parse(src), st) != got {
t.Errorf("when_long long should equal always")
}
}