Skip to content

Commit 65388d0

Browse files
committed
refactor: preserve multi-line formatting in generated doc comments
Replace SanitizeComment with FormatDocComment to maintain the original structure of JSON schema descriptions. The new approach preserves paragraph breaks and line-by-line formatting instead of collapsing multi-line descriptions into single lines. Added emitDocComment and appendDocComments helper functions to standardize doc comment emission across the code generator. Deprecated SanitizeComment in favor of the new formatting function. This improves readability of generated API documentation by maintaining the intended structure of schema descriptions.
1 parent dbba466 commit 65388d0

File tree

3 files changed

+771
-145
lines changed

3 files changed

+771
-145
lines changed

cmd/generate/internal/emit/types.go

Lines changed: 32 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,25 @@ func generateNestedTypeName(parentName, rawFieldName string, usedNames map[strin
9696
return fullName
9797
}
9898

99+
// emitDocComment emits a multi-line doc comment for the given description.
100+
// Properly handles newlines and paragraph breaks from JSON schema descriptions.
101+
func emitDocComment(f *File, desc string) {
102+
lines := util.FormatDocComment(desc)
103+
for _, line := range lines {
104+
f.Comment(line)
105+
}
106+
}
107+
108+
// appendDocComments appends doc comment Code elements to a slice for struct field comments.
109+
// Used when building struct field definitions with proper multi-line comment support.
110+
func appendDocComments(codeSlice []Code, desc string) []Code {
111+
lines := util.FormatDocComment(desc)
112+
for _, line := range lines {
113+
codeSlice = append(codeSlice, Comment(line))
114+
}
115+
return codeSlice
116+
}
117+
99118
// WriteTypesJen emits go/types_gen.go with all types and the Agent/Client interfaces.
100119
func WriteTypesJen(outDir string, schema *load.Schema, meta *load.Meta) error {
101120
f := NewFile("acp")
@@ -131,7 +150,7 @@ func WriteTypesJen(outDir string, schema *load.Schema, meta *load.Meta) error {
131150
}
132151

133152
if def.Description != "" {
134-
f.Comment(util.SanitizeComment(def.Description))
153+
emitDocComment(f, def.Description)
135154
}
136155

137156
switch {
@@ -194,7 +213,7 @@ func WriteTypesJen(outDir string, schema *load.Schema, meta *load.Meta) error {
194213

195214
// Generate the nested struct type
196215
if prop.Description != "" {
197-
f.Comment(util.SanitizeComment(prop.Description))
216+
emitDocComment(f, prop.Description)
198217
}
199218
nestedFields := []Code{}
200219
nestedReq := map[string]struct{}{}
@@ -211,7 +230,7 @@ func WriteTypesJen(outDir string, schema *load.Schema, meta *load.Meta) error {
211230
nprop := prop.Properties[npk]
212231
nfield := util.ToExportedField(npk)
213232
if nprop.Description != "" {
214-
nestedFields = append(nestedFields, Comment(util.SanitizeComment(nprop.Description)))
233+
nestedFields = appendDocComments(nestedFields, nprop.Description)
215234
}
216235
ntag := npk
217236
if _, ok := nestedReq[npk]; !ok {
@@ -246,7 +265,7 @@ func WriteTypesJen(outDir string, schema *load.Schema, meta *load.Meta) error {
246265
prop := def.Properties[pk]
247266
field := util.ToExportedField(pk)
248267
if prop.Description != "" {
249-
st = append(st, Comment(util.SanitizeComment(prop.Description)))
268+
st = appendDocComments(st, prop.Description)
250269
}
251270
tag := pk
252271
// Detect defaults generically
@@ -285,7 +304,7 @@ func WriteTypesJen(outDir string, schema *load.Schema, meta *load.Meta) error {
285304
if prop.Description != "" {
286305
st = append(st, Comment(""))
287306
}
288-
st = append(st, Comment(util.SanitizeComment(fmt.Sprintf("Defaults to %s if unset.", dp.defaultJSON))))
307+
st = append(st, Comment(fmt.Sprintf("Defaults to %s if unset.", dp.defaultJSON)))
289308
}
290309
// Use generated nested type if available, otherwise use jenTypeForOptional
291310
var fieldType Code
@@ -390,7 +409,8 @@ func WriteTypesJen(outDir string, schema *load.Schema, meta *load.Meta) error {
390409
target = &agentExperimentalMethods
391410
}
392411
if desc := methodDescription(schema, mi); desc != "" {
393-
*target = append(*target, Comment(util.SanitizeComment(desc)))
412+
// Method descriptions can be multi-line, format them properly
413+
*target = appendDocComments(*target, desc)
394414
}
395415
if mi.Notif != "" {
396416
name := ir.DispatchMethodNameForNotification(k, mi.Notif)
@@ -432,7 +452,8 @@ func WriteTypesJen(outDir string, schema *load.Schema, meta *load.Meta) error {
432452
target = &clientTerminal
433453
}
434454
if desc := methodDescription(schema, mi); desc != "" {
435-
*target = append(*target, Comment(util.SanitizeComment(desc)))
455+
// Method descriptions can be multi-line, format them properly
456+
*target = appendDocComments(*target, desc)
436457
}
437458
if mi.Notif != "" {
438459
name := ir.DispatchMethodNameForNotification(k, mi.Notif)
@@ -814,13 +835,13 @@ func emitUnion(f *File, name string, defs []*load.Definition, exactlyOne bool, u
814835
}
815836
sort.Strings(pkeys)
816837
if v.Description != "" {
817-
f.Comment(util.SanitizeComment(v.Description))
838+
emitDocComment(f, v.Description)
818839
}
819840
for _, pk := range pkeys {
820841
pDef := v.Properties[pk]
821842
field := util.ToExportedField(pk)
822843
if pDef.Description != "" {
823-
st = append(st, Comment(util.SanitizeComment(pDef.Description)))
844+
st = appendDocComments(st, pDef.Description)
824845
}
825846
tag := pk
826847
if _, ok := req[pk]; !ok {
@@ -831,7 +852,7 @@ func emitUnion(f *File, name string, defs []*load.Definition, exactlyOne bool, u
831852
} else if !isNull && !isObj && v.Title != "" {
832853
// Title-only extension types: emit description as comment
833854
if v.Description != "" {
834-
f.Comment(util.SanitizeComment(v.Description))
855+
emitDocComment(f, v.Description)
835856
}
836857
}
837858
f.Type().Id(tname).Struct(st...)
@@ -852,7 +873,7 @@ func emitUnion(f *File, name string, defs []*load.Definition, exactlyOne bool, u
852873
st := []Code{}
853874
for _, vi := range variants {
854875
if vi.description != "" {
855-
st = append(st, Comment(util.SanitizeComment(vi.description)))
876+
st = appendDocComments(st, vi.description)
856877
}
857878
st = append(st, Id(vi.fieldName).Op("*").Id(vi.typeName).Tag(map[string]string{"json": "-"}))
858879
}

cmd/generate/internal/util/util.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
)
99

1010
// SanitizeComment removes backticks and normalizes whitespace for Go comments.
11+
// Deprecated: Use FormatDocComment for proper multi-line comment formatting.
1112
func SanitizeComment(s string) string {
1213
s = strings.ReplaceAll(s, "`", "'")
1314
lines := strings.Split(s, "\n")
@@ -17,6 +18,35 @@ func SanitizeComment(s string) string {
1718
return strings.Join(lines, " ")
1819
}
1920

21+
// FormatDocComment formats a description as properly structured Go doc comment lines.
22+
// Preserves paragraph breaks (double newlines) and handles line breaks within paragraphs.
23+
// Returns slice of comment text without "//" prefix (caller should emit each as a comment line).
24+
//
25+
// Go doc comment conventions:
26+
// - Each line of comment text becomes a separate "// line"
27+
// - Blank lines (from \n\n) become "//" with no text
28+
// - First line should be a complete sentence ending with period
29+
func FormatDocComment(desc string) []string {
30+
if desc == "" {
31+
return nil
32+
}
33+
34+
// Replace backticks with single quotes (Go doc comments don't support backticks well)
35+
desc = strings.ReplaceAll(desc, "`", "'")
36+
37+
// Split into lines based on newlines from the JSON schema
38+
lines := strings.Split(desc, "\n")
39+
40+
var result []string
41+
for _, line := range lines {
42+
trimmed := strings.TrimSpace(line)
43+
// Preserve blank lines as empty strings - jen will render them as "//"
44+
result = append(result, trimmed)
45+
}
46+
47+
return result
48+
}
49+
2050
// TitleWord uppercases the first rune and lowercases the rest.
2151
func TitleWord(s string) string {
2252
if s == "" {

0 commit comments

Comments
 (0)