diff --git a/generate_test.go b/generate_test.go index d4b9bb3..754583b 100644 --- a/generate_test.go +++ b/generate_test.go @@ -46,7 +46,6 @@ func TestGenerateWithImports(t *testing.T) { ToDo: map[string]string{ "imports/oneref_verbose": "Figure out how to disambiguate struct literals from the struct-with-braces-and-one-element case", "imports/struct_shorthand": "Shorthand struct notation is currently unsupported, needs fixing", - "imports/inline_comments": "Inline comments do not appear be retrievable from cue.Value.Doc()", }, } diff --git a/generator.go b/generator.go index 1d8c987..8f2f14d 100644 --- a/generator.go +++ b/generator.go @@ -277,7 +277,6 @@ type KV struct { // if memberNames is absent, then keys implicitly generated as CamelCase // - string struct: struct keys get enum keys, struct values enum values func (g *generator) genEnum(name string, v cue.Value) []ts.Decl { - vdoc := v.Doc() // FIXME compensate for attribute-applying call to Unify() on incoming Value op, dvals := v.Expr() if op == cue.AndOp { @@ -302,7 +301,7 @@ func (g *generator) genEnum(name string, v cue.Value) []ts.Decl { ret[0] = tsast.TypeDecl{ Name: ts.Ident(name), Type: tsast.EnumType{Elems: exprs}, - CommentList: commentsForGroup(vdoc, true), + CommentList: commentsFor(v, true), Export: g.c.Export, } @@ -1433,20 +1432,23 @@ func referenceValueAs(v cue.Value, kinds ...TSType) (ts.Expr, error) { return nil, nil } -func commentsForGroup(cgs []*ast.CommentGroup, jsdoc bool) []tsast.Comment { - if cgs == nil { - return nil - } - ret := make([]tsast.Comment, 0, len(cgs)) - for _, cg := range cgs { - if cg.Line { - panic("hit it") +func commentsFor(v cue.Value, jsdoc bool) []tsast.Comment { + docs := v.Doc() + if s, ok := v.Source().(*ast.Field); ok { + for _, c := range s.Comments() { + if !c.Doc && c.Line { + docs = append(docs, c) + } } - ret = append(ret, ts.CommentFromCUEGroup(cg, jsdoc)) } - return ret -} -func commentsFor(v cue.Value, jsdoc bool) []tsast.Comment { - return commentsForGroup(v.Doc(), jsdoc) + ret := make([]tsast.Comment, 0, len(docs)) + for _, cg := range docs { + ret = append(ret, ts.CommentFromCUEGroup(ts.Comment{ + Text: cg.Text(), + Multiline: cg.Doc && !cg.Line, + JSDoc: jsdoc, + })) + } + return ret } diff --git a/testdata/imports/inline_comments.txtar b/testdata/imports/inline_comments.txtar index 962980c..dc1d5a8 100644 --- a/testdata/imports/inline_comments.txtar +++ b/testdata/imports/inline_comments.txtar @@ -35,9 +35,9 @@ export type Nested = { * tripleInner has a before comment and * it's multiline */ - tripleInner: string, + tripleInner: string; // tripleInner has an inline comment }, }, }; // Nested has an inline comment -export type SimpleOnlyInline = string; // comment on SimpleOnlyInline +export type SimpleOnlyInline = string; // comment on SimpleOnlyInline diff --git a/ts/ast/lit.go b/ts/ast/lit.go index f6c6f9a..3027559 100644 --- a/ts/ast/lit.go +++ b/ts/ast/lit.go @@ -54,9 +54,20 @@ func (o ObjectLit) innerString(aeol EOL, lvl int) string { write("{\n") for _, e := range o.Elems { + hasInlineComments := false + for _, c := range e.Comments() { + if c.Pos == CommentInline { + hasInlineComments = true + } + } indent(lvl) write(formatInner(aeol, lvl, e)) - write(eol) + if hasInlineComments { + write("\n") + } else { + write(eol) + } + } indent(lvl - 1) @@ -97,7 +108,11 @@ func formatInner(eol EOL, lvl int, n Node) string { b.WriteString(prinner(eol, lvl, n)) for ; i < len(comms) && comms[i].Pos == CommentInline; i++ { - b.WriteString(" " + comms[i].innerString(eol, lvl) + " ") + if _, ok := n.(KeyValueExpr); ok { + b.WriteString("; " + comms[i].innerString(eol, lvl)) + } else { + b.WriteString(" " + comms[i].innerString(eol, lvl)) + } } for ; i < len(comms); i++ { diff --git a/ts/comments.go b/ts/comments.go new file mode 100644 index 0000000..19752a3 --- /dev/null +++ b/ts/comments.go @@ -0,0 +1,92 @@ +package ts + +import ( + "bufio" + "strings" + + "github.com/grafana/cuetsy/ts/ast" + "github.com/kr/text" +) + +// CommentFromString takes a string input and formats it as an ast.CommentList. +// +// Line breaks are automatically inserted to minimize raggedness, with a loose +// width limit the provided lim. +// +// If the jsdoc param is true, the resulting comment will be formatted with +// JSDoc ( /** ... */ )-style. Otherwise, ordinary comment leader ( // ... ) will +// be used. +// +// The returned ast.CommentList will have the default CommentAbove position. +func CommentFromString(s string, lim int, jsdoc bool) ast.Comment { + var b strings.Builder + prefix := func() { b.WriteString("// ") } + if jsdoc { + b.WriteString("/**\n") + prefix = func() { b.WriteString(" * ") } + } + + scanner := bufio.NewScanner(strings.NewReader(text.Wrap(s, lim))) + var i int + for scanner.Scan() { + if i != 0 { + b.WriteString("\n") + } + prefix() + b.WriteString(scanner.Text()) + i++ + } + if jsdoc { + b.WriteString("\n */\n") + } + + return ast.Comment{ + Text: b.String(), + } +} + +type Comment struct { + Text string + Multiline bool + JSDoc bool +} + +// CommentFromCUEGroup creates an ast.CommentList from a Comment. +// +// Original line breaks are preserved, in keeping with principles of semantic line breaks. +func CommentFromCUEGroup(cg Comment) ast.Comment { + var b strings.Builder + pos := ast.CommentAbove + if !cg.Multiline { + pos = ast.CommentInline + } + + prefix := func() { b.WriteString("// ") } + if cg.JSDoc && cg.Multiline { + b.WriteString("/**\n") + prefix = func() { b.WriteString(" * ") } + } else { + b.WriteString("//") + prefix = func() { b.WriteString(" ") } + } + + scanner := bufio.NewScanner(strings.NewReader(cg.Text)) + var i int + for scanner.Scan() { + if i != 0 { + b.WriteString("\n") + } + prefix() + b.WriteString(scanner.Text()) + i++ + } + if cg.JSDoc && cg.Multiline { + b.WriteString("\n") + b.WriteString(" */") + } + + return ast.Comment{ + Text: b.String(), + Pos: pos, + } +} diff --git a/ts/ts.go b/ts/ts.go index 010e1f7..2125cea 100644 --- a/ts/ts.go +++ b/ts/ts.go @@ -1,12 +1,7 @@ package ts import ( - "bufio" - "strings" - - cast "cuelang.org/go/cue/ast" "github.com/grafana/cuetsy/ts/ast" - "github.com/kr/text" ) type ( @@ -98,84 +93,3 @@ func Bool(b bool) Expr { } return Ident("false") } - -// CommentFromString takes a string input and formats it as an ast.CommentList. -// -// Line breaks are automatically inserted to minimize raggedness, with a loose -// width limit the provided lim. -// -// If the jsdoc param is true, the resulting comment will be formatted with -// JSDoc ( /** ... */ )-style. Otherwise, ordinary comment leader ( // ... ) will -// be used. -// -// The returned ast.CommentList will have the default CommentAbove position. -func CommentFromString(s string, lim int, jsdoc bool) ast.Comment { - var b strings.Builder - prefix := func() { b.WriteString("// ") } - if jsdoc { - b.WriteString("/**\n") - prefix = func() { b.WriteString(" * ") } - } - - scanner := bufio.NewScanner(strings.NewReader(text.Wrap(s, lim))) - var i int - for scanner.Scan() { - if i != 0 { - b.WriteString("\n") - } - prefix() - b.WriteString(scanner.Text()) - i++ - } - if jsdoc { - b.WriteString("\n */\n") - } - - return ast.Comment{ - Text: b.String(), - } -} - -// CommentFromCUEGroup creates an ast.CommentList from a CUE AST CommentGroup. -// -// Original line breaks are preserved, in keeping with principles of semantic line breaks. -func CommentFromCUEGroup(cg *cast.CommentGroup, jsdoc bool) ast.Comment { - var b strings.Builder - pos := ast.CommentAbove - if cg.Line { - pos = ast.CommentInline - } - - prefix := func() { b.WriteString("// ") } - if jsdoc { - b.WriteString("/**") - if cg.Line { - prefix = func() { b.WriteString(" ") } - } else { - b.WriteString("\n") - prefix = func() { b.WriteString(" * ") } - } - } - - scanner := bufio.NewScanner(strings.NewReader(cg.Text())) - var i int - for scanner.Scan() { - if i != 0 { - b.WriteString("\n") - } - prefix() - b.WriteString(scanner.Text()) - i++ - } - if jsdoc { - if !cg.Line { - b.WriteString("\n") - } - b.WriteString(" */") - } - - return ast.Comment{ - Text: b.String(), - Pos: pos, - } -}