Repository: googleapis/gapic-generator-go
Pull request #183 (2019-08-29 21:29:57)

SampleGen: generate comments on fields of the request object
- when `value_is_file: true`, the comments are above reading bytes from the file.
- when the field is a part of the resource name, the comments are above constructing the resource name.
- for other fields, the comments are above the field in constructing the request object.

Comments for sample function parameters are not handled yet.

hzyi-google: cc/ @googleapis/samplegen 

Patch:
diff --git a/cmd/gen-go-sample/bytes.go b/cmd/gen-go-sample/bytes.go
index 85ff956..9c6d65e 100644
--- a/cmd/gen-go-sample/bytes.go
+++ b/cmd/gen-go-sample/bytes.go
@@ -15,6 +15,7 @@
 package main
 
 import (
+	"bufio"
 	"bytes"
 	"fmt"
 	"text/scanner"
@@ -34,8 +35,12 @@ func bytesFmt() func(*generator, string) (string, error) {
 type fileInfo struct {
 	// fileName is the text of the file name. If it's a string literal, it's already quoted.
 	fileName string
+
 	// varName is the name of the local variable to hold the bytes of the file.
 	varName string
+
+	// comment is the user-specified comments that will be generated above this code
+	comment string
 }
 
 const fileContentSuffix = "Bytes"
@@ -82,6 +87,9 @@ func handleReadFile(info *fileInfo, buf *bytes.Buffer, g *generator) {
 	fn := info.fileName
 	g.imports[pbinfo.ImportSpec{Path: "io/ioutil"}] = true
 
+	w := bufio.NewWriter(buf)
+	printCommentLines(w, info.comment, 0)
+	w.Flush()
 	fmt.Fprintf(buf, "%s, err := ioutil.ReadFile(%s)\n", vn, fn)
 	fmt.Fprintf(buf, "if err != nil {\n")
 	fmt.Fprintf(buf, "\treturn err\n")
diff --git a/cmd/gen-go-sample/inittree.go b/cmd/gen-go-sample/inittree.go
index 50bf1ff..7846e08 100644
--- a/cmd/gen-go-sample/inittree.go
+++ b/cmd/gen-go-sample/inittree.go
@@ -89,6 +89,9 @@ type initTree struct {
 	// Text of the literal. If the literal is a string, it's already quoted.
 	leafVal string
 
+	// comment right above the field in the initialization expression
+	comment string
+
 	// parent tracks the parent type of this type.
 	// The parent of root is nil.
 	parent *initTree
@@ -239,7 +242,7 @@ func (t *initTree) indexMap(k string) *initTree {
 // parseInit adds a node to this initTree with given a field path and the default value
 // of the node.
 // TODO(hzyi): allow map index.
-func (t *initTree) parseInit(path string, value string, info pbinfo.Info) error {
+func (t *initTree) parseInit(path string, value string, comment string, info pbinfo.Info) error {
 	// The first ident is treated specially to be a member of the root.
 	// Since we know the root is a struct, a dot is the only legal token anyway,
 	// so just insert a dot here so we don't need to treat the first token specially.
@@ -290,6 +293,7 @@ func (t *initTree) parseInit(path string, value string, info pbinfo.Info) error
 		}
 	}
 	t.leafVal = value
+	t.comment = comment
 	return report(nil)
 }
 
@@ -478,12 +482,6 @@ func (t *initTree) print(w *bufio.Writer, g *generator, ind int) error {
 		}
 	}
 
-	indent := func(j int) {
-		for i := 0; i < j; i++ {
-			w.WriteByte('\t')
-		}
-	}
-
 	tvals := t.vals
 	writeKey := true
 	if t.typ.repeated {
@@ -524,13 +522,19 @@ func (t *initTree) print(w *bufio.Writer, g *generator, ind int) error {
 	}
 
 	for i, k := range t.keys {
-		indent(ind + 1)
+		printCommentLines(w, t.vals[i].comment, ind + 1)
+		if t.vals[i].typ.namePat != nil {
+			for _, v := range t.vals[i].vals {
+				printCommentLines(w, v.comment, ind + 1)
+			}
+		}
+		indent(w, ind + 1)
 
 		var closeBrace bool
 		if oneof, ok := oneofs[k]; ok {
 			fmt.Fprintf(w, "%s: &%s.%s_%s{\n", snakeToPascal(oneof), impSpec.Name, typName, snakeToPascal(k))
 			closeBrace = true
-			indent(ind + 2)
+			indent(w, ind + 2)
 		}
 
 		if writeKey {
@@ -544,14 +548,14 @@ func (t *initTree) print(w *bufio.Writer, g *generator, ind int) error {
 
 		if closeBrace {
 			w.WriteString(",\n")
-			indent(ind + 1)
+			indent(w, ind + 1)
 			w.WriteByte('}')
 		}
 		w.WriteString(",\n")
 	}
 
 	if len(t.keys) != 0 {
-		indent(ind)
+		indent(w, ind)
 	}
 	w.WriteString("}")
 	return nil
@@ -588,3 +592,21 @@ func snakeToCamel(s string) string {
 	}
 	return sb.String()
 }
+
+func printCommentLines(w *bufio.Writer, comment string, ind int) {
+	if comment == "" {
+		return
+	}
+	for _, c := range strings.Split(strings.TrimRight(comment, "\n"), "\n") {
+		indent(w, ind)
+		w.WriteString("// ")
+		w.WriteString(c)
+		w.WriteByte('\n')
+	}
+}
+
+func indent(w *bufio.Writer, ind int) {
+	for i := 0; i < ind; i++ {
+		w.WriteByte('\t')
+	}
+}
diff --git a/cmd/gen-go-sample/main.go b/cmd/gen-go-sample/main.go
index 871c44c..03f8c9e 100644
--- a/cmd/gen-go-sample/main.go
+++ b/cmd/gen-go-sample/main.go
@@ -453,7 +453,7 @@ func (g *generator) getInitInfo(inType pbinfo.ProtoType, methConf GAPICMethod, f
 
 	// Set up request object.
 	for _, fieldConf := range fieldConfs {
-		if err := itree.parseInit(fieldConf.Field, fieldConf.Value, g.descInfo); err != nil {
+		if err := itree.parseInit(fieldConf.Field, fieldConf.Value, fieldConf.Comment, g.descInfo); err != nil {
 			return initInfo{}, errors.E(err, "can't set default value: %s=%s", fieldConf.Field, fieldConf.Value)
 		}
 	}
@@ -496,7 +496,7 @@ func (g *generator) getInitInfo(inType pbinfo.ProtoType, methConf GAPICMethod, f
 				flagNames = append(flagNames, fieldConf.InputParameter)
 				argTrees = append(argTrees, subTree)
 			}
-			files = append(files, &fileInfo{fileName, varName})
+			files = append(files, &fileInfo{fileName, varName, fieldConf.Comment})
 			continue
 		}
 


Test patch:
diff --git a/cmd/gen-go-sample/inittree_test.go b/cmd/gen-go-sample/inittree_test.go
index 5d693e9..255030a 100644
--- a/cmd/gen-go-sample/inittree_test.go
+++ b/cmd/gen-go-sample/inittree_test.go
@@ -57,7 +57,7 @@ func TestTree(t *testing.T) {
 		typ: initType{desc: info.Type["RootType"]},
 	}
 	for _, fv := range fieldVals {
-		if err := root.parseInit(fv[0], fv[1], info); err != nil {
+		if err := root.parseInit(fv[0], fv[1], "", info); err != nil {
 			t.Fatal(err)
 		}
 	}
@@ -106,7 +106,7 @@ testcase:
 	} {
 		root := initTree{typ: initType{desc: info.Type["RootType"]}}
 		for _, txt := range tst {
-			if root.parseInit(txt[0], txt[1], info) != nil {
+			if root.parseInit(txt[0], txt[1], "", info) != nil {
 				continue testcase
 			}
 		}
diff --git a/cmd/gen-go-sample/main_test.go b/cmd/gen-go-sample/main_test.go
index 902786d..2161d99 100644
--- a/cmd/gen-go-sample/main_test.go
+++ b/cmd/gen-go-sample/main_test.go
@@ -39,19 +39,19 @@ func TestUnary(t *testing.T) {
 		RegionTag: "awesome_region",
 		Request: []schema_v1p2.RequestConfig{
 			{Field: "a.x", Value: "42", InputParameter: "the_x"},
-			{Field: "a.y", Value: "3.14159"},
+			{Field: "a.y", Value: "3.14159", Comment: "approximation of Pi"},
 			{Field: "b", Value: "foobar", InputParameter: "the_b"},
 			{Field: "e", Value: "BANANA"},
 			{Field: "f", Value: "in a oneof"},
 			{Field: "bytes", Value: "mybytes"},
-			{Field: "data_alice", Value: "path/to/local/file/alice.txt", ValueIsFile: true},
-			{Field: "a_array[0].x", Value: "0"},
+			{Field: "data_alice", Value: "path/to/local/file/alice.txt", ValueIsFile: true, Comment: "the path of a local file"},
+			{Field: "a_array[0].x", Value: "0", Comment: "initializing an array element"},
 			{Field: "a_array[0].y", Value: "1"},
-			{Field: "a_array[1].x", Value: "2"},
+			{Field: "a_array[1].x", Value: "2", Comment: "this is a\nmulti-line comment\n"},
 			{Field: "a_array[1].y", Value: "3"},
 			{Field: "resource_field%foo", Value: "myfoo", InputParameter: "the_foo"},
 			{Field: "data_bob", Value: "path/to/local/file/bob.txt", ValueIsFile: true, InputParameter: "bob_file"},
-			{Field: "resource_field%bar", Value: "mybar"},
+			{Field: "resource_field%bar", Value: "mybar", Comment: "construct a resource name"},
 		},
 		Response: []schema_v1p2.ResponseConfig{
 			{Define: "out_a = $resp.a"},
diff --git a/cmd/gen-go-sample/testdata/sample_unary.want b/cmd/gen-go-sample/testdata/sample_unary.want
index 801f8b3..ad22a6c 100644
--- a/cmd/gen-go-sample/testdata/sample_unary.want
+++ b/cmd/gen-go-sample/testdata/sample_unary.want
@@ -39,6 +39,7 @@ func sampleUnaryMethod(theX int64, theB string, theFoo string, bobFile string) e
 	// theB := "foobar"
 	// theFoo := "myfoo"
 	// bobFile := "path/to/local/file/bob.txt"
+	// the path of a local file
 	dataAlice, err := ioutil.ReadFile("path/to/local/file/alice.txt")
 	if err != nil {
 		return err
@@ -50,9 +51,11 @@ func sampleUnaryMethod(theX int64, theB string, theFoo string, bobFile string) e
 	}
 
 	req := &foopb.InputType{
+		// construct a resource name
 		ResourceField: fmt.Sprintf("foos/%s/bars/%s", theFoo, "mybar"),
 		A: &foopb.AType{
 			X: theX,
+			// approximation of Pi
 			Y: 3.14159,
 		},
 		B: theB,
@@ -64,10 +67,13 @@ func sampleUnaryMethod(theX int64, theB string, theFoo string, bobFile string) e
 		DataAlice: dataAlice,
 		AArray: []*foopb.AType{
 			&foopb.AType{
+				// initializing an array element
 				X: 0,
 				Y: 1,
 			},
 			&foopb.AType{
+				// this is a
+				// multi-line comment
 				X: 2,
 				Y: 3,
 			},