cue/format: formatting of nested single-line fields

Change-Id: Ib2982329958ab1d13bdd018728eeebafc122bc8b
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/3789
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cmd/cue/cmd/fix_test.go b/cmd/cue/cmd/fix_test.go
index 290b326..88ab4b3 100644
--- a/cmd/cue/cmd/fix_test.go
+++ b/cmd/cue/cmd/fix_test.go
@@ -73,6 +73,7 @@
 for k, v in src {
 	"\(k)": v
 }
+
 // foo
 // bar
 for k, v in src {
diff --git a/cmd/cue/cmd/testdata/script/import_path.txt b/cmd/cue/cmd/testdata/script/import_path.txt
index 64a4f32..18aacdb 100644
--- a/cmd/cue/cmd/testdata/script/import_path.txt
+++ b/cmd/cue/cmd/testdata/script/import_path.txt
@@ -1,7 +1,6 @@
 cue import -o - -f -l '"\(strings.ToLower(kind))" "\(name)"' ./import
 cmp stdout expect-stdout
 -- expect-stdout --
-
 service: booster: {
 	kind: "Service"
 	name: "booster"
diff --git a/cue/format/format.go b/cue/format/format.go
index 6b2c6e2..99767c0 100644
--- a/cue/format/format.go
+++ b/cue/format/format.go
@@ -173,14 +173,6 @@
 	stack    []frame
 	current  frame
 	nestExpr int
-
-	labelBuf []labelEntry
-}
-
-type labelEntry struct {
-	label    ast.Label
-	typ      token.Token
-	optional bool
 }
 
 func newFormatter(p *printer) *formatter {
diff --git a/cue/format/node.go b/cue/format/node.go
index 8524431..7369b6d 100644
--- a/cue/format/node.go
+++ b/cue/format/node.go
@@ -57,13 +57,65 @@
 
 // Helper functions for common node lists. They may be empty.
 
+func nestDepth(f *ast.Field) int {
+	d := 1
+	if s, ok := f.Value.(*ast.StructLit); ok {
+		switch {
+		case len(s.Elts) != 1:
+			d = 0
+		default:
+			if f, ok := s.Elts[0].(*ast.Field); ok {
+				d += nestDepth(f)
+			}
+		}
+	}
+	return d
+}
+
+// TODO: be more accurate and move to astutil
+func hasDocComments(d ast.Decl) bool {
+	if len(d.Comments()) > 0 {
+		return true
+	}
+	switch x := d.(type) {
+	case *ast.Field:
+		return len(x.Label.Comments()) > 0
+	case *ast.Alias:
+		return len(x.Ident.Comments()) > 0
+	}
+	return false
+}
+
 func (f *formatter) walkDeclList(list []ast.Decl) {
 	f.before(nil)
+	d := 0
 	for i, x := range list {
 		if i > 0 {
 			f.print(declcomma)
+			nd := 0
+			if f, ok := x.(*ast.Field); ok {
+				nd = nestDepth(f)
+			}
+			if f.current.parentSep == newline && (d == 0 || nd != d) {
+				f.print(f.formfeed())
+			}
+			if hasDocComments(x) {
+				switch x := list[i-1].(type) {
+				case *ast.Field:
+					if x.Token == token.ISA {
+						f.print(newsection)
+					}
+
+				default:
+					f.print(newsection)
+				}
+			}
 		}
 		f.decl(x)
+		d = 0
+		if f, ok := x.(*ast.Field); ok {
+			d = nestDepth(f)
+		}
 		if j := i + 1; j < len(list) {
 			switch x := list[j].(type) {
 			case *ast.Field:
@@ -125,67 +177,76 @@
 	f.after(file)
 	f.print(token.EOF)
 }
+
+func (f *formatter) inlineField(n *ast.Field) *ast.Field {
+	regular := isRegularField(n.Token)
+	// shortcut single-element structs.
+	// If the label has a valid position, we assume that an unspecified
+	// Lbrace signals the intend to collapse fields.
+	if !n.Label.Pos().IsValid() && !(f.printer.cfg.simplify && regular) {
+		return nil
+	}
+
+	obj, ok := n.Value.(*ast.StructLit)
+	if !ok || len(obj.Elts) != 1 ||
+		(obj.Lbrace.IsValid() && !f.printer.cfg.simplify) ||
+		len(n.Attrs) > 0 {
+		return nil
+	}
+
+	mem, ok := obj.Elts[0].(*ast.Field)
+	if !ok || len(mem.Attrs) > 0 {
+		return nil
+	}
+
+	if hasDocComments(mem) {
+		// TODO: this inserts curly braces even in spaces where this
+		// may not be desirable, such as:
+		// a:
+		//   // foo
+		//   b: 3
+		return nil
+	}
+	return mem
+}
+
 func (f *formatter) decl(decl ast.Decl) {
+
 	if decl == nil {
 		return
 	}
+	defer f.after(decl)
 	if !f.before(decl) {
-		goto after
+		return
 	}
+
 	switch n := decl.(type) {
 	case *ast.Field:
-		// shortcut single-element structs.
-		lastSize := len(f.labelBuf)
-		f.labelBuf = f.labelBuf[:0]
+		f.label(n.Label, n.Optional != token.NoPos)
+
 		regular := isRegularField(n.Token)
-		first, typ, opt := n.Label, n.Token, n.Optional != token.NoPos
-
-		// If the label has a valid position, we assume that an unspecified
-		// Lbrace signals the intend to collapse fields.
-		for n.Label.Pos().IsValid() || (f.printer.cfg.simplify && regular) {
-			obj, ok := n.Value.(*ast.StructLit)
-			if !ok || len(obj.Elts) != 1 || (obj.Lbrace.IsValid() && !f.printer.cfg.simplify) || len(n.Attrs) > 0 {
-				break
-			}
-
-			// Verify that struct doesn't have inside comments and that
-			// element doesn't have doc comments.
-			hasComments := len(obj.Elts[0].Comments()) > 0
-			for _, c := range obj.Comments() {
-				if c.Position == 1 || c.Position == 2 {
-					hasComments = true
-				}
-			}
-			if hasComments {
-				break
-			}
-
-			mem, ok := obj.Elts[0].(*ast.Field)
-			if !ok || len(mem.Attrs) > 0 {
-				break
-			}
-			entry := labelEntry{mem.Label, mem.Token, mem.Optional != token.NoPos}
-			f.labelBuf = append(f.labelBuf, entry)
-			n = mem
+		if regular {
+			f.print(n.TokenPos, token.COLON)
+		} else {
+			f.print(blank, nooverride, n.Token)
 		}
 
-		if lastSize != len(f.labelBuf) {
-			f.print(formfeed)
-		}
+		if mem := f.inlineField(n); mem != nil {
+			switch {
+			default:
+				fallthrough
 
-		f.before(nil)
-		f.label(first, opt)
-		for _, x := range f.labelBuf {
-			if isRegularField(typ) {
-				f.print(n.TokenPos, token.COLON)
-			} else {
-				f.print(blank, nooverride, typ)
+			case regular && f.cfg.simplify:
+				f.print(blank, nooverride)
+				f.decl(mem)
+
+			case mem.Label.Pos().IsNewline():
+				f.print(indent, formfeed)
+				f.decl(mem)
+				f.indent--
 			}
-			f.print(blank, nooverride)
-			f.label(x.label, x.optional)
-			typ = x.typ
+			return
 		}
-		f.after(nil)
 
 		nextFF := f.nextNeedsFormfeed(n.Value)
 		tab := vtab
@@ -193,11 +254,8 @@
 			tab = blank
 		}
 
-		if isRegularField(n.Token) {
-			f.print(n.TokenPos, token.COLON, tab)
-		} else {
-			f.print(blank, nooverride, n.Token, tab)
-		}
+		f.print(tab)
+
 		if n.Value != nil {
 			switch n.Value.(type) {
 			case *ast.ListComprehension, *ast.ListLit, *ast.StructLit:
@@ -278,15 +336,13 @@
 		f.expr(n.Ident)
 		f.print(blank, n.Equal, token.BIND, blank)
 		f.expr(n.Expr)
-		f.print(declcomma, newline) // implied
+		f.print(declcomma) // implied
 
 	case *ast.CommentGroup:
 		f.print(newsection)
 		f.printComment(n)
 		f.print(newsection)
 	}
-after:
-	f.after(decl)
 }
 
 func (f *formatter) nextNeedsFormfeed(n ast.Expr) bool {
@@ -325,6 +381,8 @@
 }
 
 func (f *formatter) label(l ast.Label, optional bool) {
+	f.before(l)
+	defer f.after(l)
 	switch n := l.(type) {
 	case *ast.Ident:
 		// Escape an identifier that has invalid characters. This may happen,
diff --git a/cue/format/node_test.go b/cue/format/node_test.go
index 6a5fe3e..dde17d9 100644
--- a/cue/format/node_test.go
+++ b/cue/format/node_test.go
@@ -41,8 +41,7 @@
 			}},
 		}},
 		// Force a new struct.
-		out: `
-foo: bar :: {
+		out: `foo: bar :: {
 }`,
 	}}
 	for _, tc := range testCases {
diff --git a/cue/format/testdata/expressions.golden b/cue/format/testdata/expressions.golden
index cbd4ab3..9aa3e5d 100644
--- a/cue/format/testdata/expressions.golden
+++ b/cue/format/testdata/expressions.golden
@@ -18,6 +18,14 @@
 
 	bottom: _|_
 
+	a:
+		b:
+			c: 2
+
+	a: bbbb: c: 3
+	a: b: 3
+	a: bb: cc: 3
+
 	empty: {}
 	emptyNewLine: {
 
diff --git a/cue/format/testdata/expressions.input b/cue/format/testdata/expressions.input
index 2c65b4b..0908cc0 100644
--- a/cue/format/testdata/expressions.input
+++ b/cue/format/testdata/expressions.input
@@ -18,6 +18,14 @@
 
     bottom: _|_
 
+    a:
+    b:
+    c: 2
+
+    a: bbbb: c: 3
+    a: b: 3
+    a: bb: cc: 3
+
     empty: {}
     emptyNewLine: {
 
diff --git a/cue/format/testdata/simplify.golden b/cue/format/testdata/simplify.golden
index c6682e0..0b5c393 100644
--- a/cue/format/testdata/simplify.golden
+++ b/cue/format/testdata/simplify.golden
@@ -1,6 +1,7 @@
-
 foo: bar: "str"
 
 a: B: 42
 
 "a.b": "foo-": cc_dd: x
+
+a: b: c: 3
diff --git a/cue/format/testdata/simplify.input b/cue/format/testdata/simplify.input
index c6dd770..c743830 100644
--- a/cue/format/testdata/simplify.input
+++ b/cue/format/testdata/simplify.input
@@ -3,3 +3,8 @@
 a "B": 42
 
 "a.b" "foo-" "cc_dd": x
+
+
+a:
+    b:
+        c: 3
\ No newline at end of file
diff --git a/cue/parser/parser_test.go b/cue/parser/parser_test.go
index 51821d9..0c4f1d8 100644
--- a/cue/parser/parser_test.go
+++ b/cue/parser/parser_test.go
@@ -328,10 +328,15 @@
 
 		// about c
 
+		// about d
+		d:
+			// about e
+			e: 3
 		`,
 		"<[d0// a ...] [l5// line a] [5// about a] a: 5>, " +
 			"<[d0// b ...] [l2// lineb] [5// about b] b: 6>, " +
-			"<[5// about c] c: 7>",
+			"<[5// about c] c: 7>, " +
+			"<[d0// about d] d: {<[d0// about e] e>: 3}>",
 	}, {
 		"expr comments",
 		`
diff --git a/doc/tutorial/kubernetes/quick/pkg/k8s.io/api/apps/v1beta1/types_go_gen.cue b/doc/tutorial/kubernetes/quick/pkg/k8s.io/api/apps/v1beta1/types_go_gen.cue
index f49b235..fd4c052 100644
--- a/doc/tutorial/kubernetes/quick/pkg/k8s.io/api/apps/v1beta1/types_go_gen.cue
+++ b/doc/tutorial/kubernetes/quick/pkg/k8s.io/api/apps/v1beta1/types_go_gen.cue
@@ -44,6 +44,7 @@
 // Scale represents a scaling request for a resource.
 Scale :: {
 	metav1.TypeMeta
+
 	// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
 	// +optional
 	metadata?: metav1.ObjectMeta @go(ObjectMeta) @protobuf(1,bytes,opt)
@@ -67,6 +68,7 @@
 // map to the same storage identity.
 StatefulSet :: {
 	metav1.TypeMeta
+
 	// +optional
 	metadata?: metav1.ObjectMeta @go(ObjectMeta) @protobuf(1,bytes,opt)
 
@@ -268,6 +270,7 @@
 // StatefulSetList is a collection of StatefulSets.
 StatefulSetList :: {
 	metav1.TypeMeta
+
 	// +optional
 	metadata?: metav1.ListMeta @go(ListMeta) @protobuf(1,bytes,opt)
 	items: [...StatefulSet] @go(Items,[]StatefulSet) @protobuf(2,bytes,rep)
@@ -278,6 +281,7 @@
 // Deployment enables declarative updates for Pods and ReplicaSets.
 Deployment :: {
 	metav1.TypeMeta
+
 	// Standard object metadata.
 	// +optional
 	metadata?: metav1.ObjectMeta @go(ObjectMeta) @protobuf(1,bytes,opt)
@@ -345,6 +349,7 @@
 // DeploymentRollback stores the information required to rollback a deployment.
 DeploymentRollback :: {
 	metav1.TypeMeta
+
 	// Required: This must match the Name of a deployment.
 	name: string @go(Name) @protobuf(1,bytes,opt)
 
@@ -510,6 +515,7 @@
 // DeploymentList is a list of Deployments.
 DeploymentList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// +optional
 	metadata?: metav1.ListMeta @go(ListMeta) @protobuf(1,bytes,opt)
@@ -531,6 +537,7 @@
 // depend on its stability. It is primarily for internal use by controllers.
 ControllerRevision :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -546,6 +553,7 @@
 // ControllerRevisionList is a resource containing a list of ControllerRevision objects.
 ControllerRevisionList :: {
 	metav1.TypeMeta
+
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
 	metadata?: metav1.ListMeta @go(ListMeta) @protobuf(1,bytes,opt)
diff --git a/doc/tutorial/kubernetes/quick/pkg/k8s.io/api/core/v1/types_go_gen.cue b/doc/tutorial/kubernetes/quick/pkg/k8s.io/api/core/v1/types_go_gen.cue
index fcd6299..ce7e297 100644
--- a/doc/tutorial/kubernetes/quick/pkg/k8s.io/api/core/v1/types_go_gen.cue
+++ b/doc/tutorial/kubernetes/quick/pkg/k8s.io/api/core/v1/types_go_gen.cue
@@ -303,6 +303,7 @@
 // More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes
 PersistentVolume :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -433,6 +434,7 @@
 // PersistentVolumeList is a list of PersistentVolume items.
 PersistentVolumeList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -446,6 +448,7 @@
 // PersistentVolumeClaim is a user's request for and claim to a persistent volume
 PersistentVolumeClaim :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -466,6 +469,7 @@
 // PersistentVolumeClaimList is a list of PersistentVolumeClaim items.
 PersistentVolumeClaimList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -1261,6 +1265,7 @@
 // mode.
 SecretProjection :: {
 	LocalObjectReference
+
 	// If unspecified, each key-value pair in the Data field of the referenced
 	// Secret will be projected into the volume as a file whose name is the
 	// key and content is the value. If specified, the listed keys will be
@@ -1729,6 +1734,7 @@
 // ConfigMap volumes support ownership management and SELinux relabeling.
 ConfigMapVolumeSource :: {
 	LocalObjectReference
+
 	// If unspecified, each key-value pair in the Data field of the referenced
 	// ConfigMap will be projected into the volume as a file whose name is the
 	// key and content is the value. If specified, the listed keys will be
@@ -1763,6 +1769,7 @@
 // mode.
 ConfigMapProjection :: {
 	LocalObjectReference
+
 	// If unspecified, each key-value pair in the Data field of the referenced
 	// ConfigMap will be projected into the volume as a file whose name is the
 	// key and content is the value. If specified, the listed keys will be
@@ -2134,6 +2141,7 @@
 // Selects a key from a ConfigMap.
 ConfigMapKeySelector :: {
 	LocalObjectReference
+
 	// The key to select.
 	key: string @go(Key) @protobuf(2,bytes,opt)
 
@@ -2145,6 +2153,7 @@
 // SecretKeySelector selects a key of a Secret.
 SecretKeySelector :: {
 	LocalObjectReference
+
 	// The key of the secret to select from.  Must be a valid secret key.
 	key: string @go(Key) @protobuf(2,bytes,opt)
 
@@ -2175,6 +2184,7 @@
 // key-value pairs as environment variables.
 ConfigMapEnvSource :: {
 	LocalObjectReference
+
 	// Specify whether the ConfigMap must be defined
 	// +optional
 	optional?: null | bool @go(Optional,*bool) @protobuf(2,varint,opt)
@@ -2187,6 +2197,7 @@
 // key-value pairs as environment variables.
 SecretEnvSource :: {
 	LocalObjectReference
+
 	// Specify whether the Secret must be defined
 	// +optional
 	optional?: null | bool @go(Optional,*bool) @protobuf(2,varint,opt)
@@ -2267,6 +2278,7 @@
 // alive or ready to receive traffic.
 Probe :: {
 	Handler
+
 	// Number of seconds after the container has started before liveness probes are initiated.
 	// More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes
 	// +optional
@@ -3770,6 +3782,7 @@
 // This is an alpha feature enabled by the EphemeralContainers feature flag.
 EphemeralContainer :: {
 	EphemeralContainerCommon
+
 	// If set, the name of the container from PodSpec that this ephemeral container targets.
 	// The ephemeral container will be run in the namespaces (IPC, PID, etc) of this container.
 	// If not set then the ephemeral container is run in whatever namespaces are shared
@@ -3878,6 +3891,7 @@
 // PodStatusResult is a wrapper for PodStatus returned by kubelet that can be encode/decoded
 PodStatusResult :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -3896,6 +3910,7 @@
 // by clients and scheduled onto hosts.
 Pod :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -3918,6 +3933,7 @@
 // PodList is a list of Pods.
 PodList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -3944,6 +3960,7 @@
 // PodTemplate describes a template for creating copies of a predefined pod.
 PodTemplate :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -3958,6 +3975,7 @@
 // PodTemplateList is a list of PodTemplates.
 PodTemplateList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -4061,6 +4079,7 @@
 // ReplicationController represents the configuration of a replication controller.
 ReplicationController :: {
 	metav1.TypeMeta
+
 	// If the Labels of a ReplicationController are empty, they are defaulted to
 	// be the same as the Pod(s) that the replication controller manages.
 	// Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
@@ -4084,6 +4103,7 @@
 // ReplicationControllerList is a collection of replication controllers.
 ReplicationControllerList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -4380,6 +4400,7 @@
 // will answer requests sent through the proxy.
 Service :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -4405,6 +4426,7 @@
 // ServiceList holds a list of services.
 ServiceList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -4420,6 +4442,7 @@
 // * a set of secrets
 ServiceAccount :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -4448,6 +4471,7 @@
 // ServiceAccountList is a list of ServiceAccount objects
 ServiceAccountList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -4472,6 +4496,7 @@
 //  ]
 Endpoints :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -4560,6 +4585,7 @@
 // EndpointsList is a list of endpoints.
 EndpointsList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -5000,6 +5026,7 @@
 // Each node will have a unique identifier in the cache (i.e. in etcd).
 Node :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -5021,6 +5048,7 @@
 // NodeList is the whole list of all Nodes which have been registered with master.
 NodeList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -5118,6 +5146,7 @@
 // Use of multiple namespaces is optional.
 Namespace :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -5137,6 +5166,7 @@
 // NamespaceList is a list of Namespaces.
 NamespaceList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -5151,6 +5181,7 @@
 // Deprecated in 1.7, please use the bindings subresource of pods instead.
 Binding :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -5163,6 +5194,7 @@
 // A list of ephemeral containers used with the Pod ephemeralcontainers subresource.
 EphemeralContainers :: {
 	metav1.TypeMeta
+
 	// +optional
 	metadata?: metav1.ObjectMeta @go(ObjectMeta) @protobuf(1,bytes,opt)
 
@@ -5185,6 +5217,7 @@
 // PodLogOptions is the query options for a Pod's logs REST call.
 PodLogOptions :: {
 	metav1.TypeMeta
+
 	// The container for which to stream logs. Defaults to only container if there is one container in the pod.
 	// +optional
 	container?: string @go(Container) @protobuf(1,bytes,opt)
@@ -5234,6 +5267,7 @@
 // and also when we cut V2, we should export a "StreamOptions" or somesuch that contains Stdin, Stdout, Stder and TTY
 PodAttachOptions :: {
 	metav1.TypeMeta
+
 	// Stdin if true, redirects the standard input stream of the pod for this call.
 	// Defaults to false.
 	// +optional
@@ -5268,6 +5302,7 @@
 // and also when we cut V2, we should export a "StreamOptions" or somesuch that contains Stdin, Stdout, Stder and TTY
 PodExecOptions :: {
 	metav1.TypeMeta
+
 	// Redirect the standard input stream of the pod for this call.
 	// Defaults to false.
 	// +optional
@@ -5305,6 +5340,7 @@
 // to be passed in the `port` header as part of request.
 PodPortForwardOptions :: {
 	metav1.TypeMeta
+
 	// List of ports to forward
 	// Required when using WebSockets
 	// +optional
@@ -5314,6 +5350,7 @@
 // PodProxyOptions is the query options to a Pod's proxy call.
 PodProxyOptions :: {
 	metav1.TypeMeta
+
 	// Path is the URL path to use for the current proxy request to pod.
 	// +optional
 	path?: string @go(Path) @protobuf(1,bytes,opt)
@@ -5322,6 +5359,7 @@
 // NodeProxyOptions is the query options to a Node's proxy call.
 NodeProxyOptions :: {
 	metav1.TypeMeta
+
 	// Path is the URL path to use for the current proxy request to node.
 	// +optional
 	path?: string @go(Path) @protobuf(1,bytes,opt)
@@ -5330,6 +5368,7 @@
 // ServiceProxyOptions is the query options to a Service's proxy call.
 ServiceProxyOptions :: {
 	metav1.TypeMeta
+
 	// Path is the part of URLs that include service endpoints, suffixes,
 	// and parameters to use for the current proxy request to service.
 	// For example, the whole request URL is
@@ -5412,6 +5451,7 @@
 // SerializedReference is a reference to serialized object.
 SerializedReference :: {
 	metav1.TypeMeta
+
 	// The reference to an object in the system.
 	// +optional
 	reference?: ObjectReference @go(Reference) @protobuf(1,bytes,opt)
@@ -5437,6 +5477,7 @@
 // Event is a report of an event somewhere in the cluster.
 Event :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	metadata: metav1.ObjectMeta @go(ObjectMeta) @protobuf(1,bytes,opt)
@@ -5528,6 +5569,7 @@
 // EventList is a list of events.
 EventList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -5593,6 +5635,7 @@
 // LimitRange sets resource usage limits for each kind of resource in a Namespace.
 LimitRange :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -5607,6 +5650,7 @@
 // LimitRangeList is a list of LimitRange items.
 LimitRangeList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -5772,6 +5816,7 @@
 // ResourceQuota sets aggregate quota restrictions enforced per namespace
 ResourceQuota :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -5791,6 +5836,7 @@
 // ResourceQuotaList is a list of ResourceQuota items.
 ResourceQuotaList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -5805,6 +5851,7 @@
 // the Data field must be less than MaxSecretSize bytes.
 Secret :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -5937,6 +5984,7 @@
 // SecretList is a list of Secret.
 SecretList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -5950,6 +5998,7 @@
 // ConfigMap holds configuration data for pods to consume.
 ConfigMap :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -5977,6 +6026,7 @@
 // ConfigMapList is a resource containing a list of ConfigMap objects.
 ConfigMapList :: {
 	metav1.TypeMeta
+
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
 	metadata?: metav1.ListMeta @go(ListMeta) @protobuf(1,bytes,opt)
@@ -6017,6 +6067,7 @@
 // ComponentStatus (and ComponentStatusList) holds the cluster validation info.
 ComponentStatus :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -6032,6 +6083,7 @@
 // Status of all the conditions for the component as a list of ComponentStatus objects.
 ComponentStatusList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -6224,6 +6276,7 @@
 // RangeAllocation is not a public type.
 RangeAllocation :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
diff --git a/doc/tutorial/kubernetes/quick/pkg/k8s.io/api/extensions/v1beta1/types_go_gen.cue b/doc/tutorial/kubernetes/quick/pkg/k8s.io/api/extensions/v1beta1/types_go_gen.cue
index bcf4692..71be27c 100644
--- a/doc/tutorial/kubernetes/quick/pkg/k8s.io/api/extensions/v1beta1/types_go_gen.cue
+++ b/doc/tutorial/kubernetes/quick/pkg/k8s.io/api/extensions/v1beta1/types_go_gen.cue
@@ -39,6 +39,7 @@
 // represents a scaling request for a resource.
 Scale :: {
 	metav1.TypeMeta
+
 	// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
 	// +optional
 	metadata?: metav1.ObjectMeta @go(ObjectMeta) @protobuf(1,bytes,opt)
@@ -62,6 +63,7 @@
 // Deployment enables declarative updates for Pods and ReplicaSets.
 Deployment :: {
 	metav1.TypeMeta
+
 	// Standard object metadata.
 	// +optional
 	metadata?: metav1.ObjectMeta @go(ObjectMeta) @protobuf(1,bytes,opt)
@@ -132,6 +134,7 @@
 // DeploymentRollback stores the information required to rollback a deployment.
 DeploymentRollback :: {
 	metav1.TypeMeta
+
 	// Required: This must match the Name of a deployment.
 	name: string @go(Name) @protobuf(1,bytes,opt)
 
@@ -297,6 +300,7 @@
 // DeploymentList is a list of Deployments.
 DeploymentList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// +optional
 	metadata?: metav1.ListMeta @go(ListMeta) @protobuf(1,bytes,opt)
@@ -474,6 +478,7 @@
 // DaemonSet represents the configuration of a daemon set.
 DaemonSet :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -507,6 +512,7 @@
 // DaemonSetList is a collection of daemon sets.
 DaemonSetList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -523,6 +529,7 @@
 // DEPRECATED - This group version of Ingress is deprecated by networking.k8s.io/v1beta1 Ingress. See the release notes for more information.
 Ingress :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -542,6 +549,7 @@
 // IngressList is a collection of Ingress.
 IngressList :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -672,6 +680,7 @@
 // ReplicaSet ensures that a specified number of pod replicas are running at any given time.
 ReplicaSet :: {
 	metav1.TypeMeta
+
 	// If the Labels of a ReplicaSet are empty, they are defaulted to
 	// be the same as the Pod(s) that the ReplicaSet manages.
 	// Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
@@ -695,6 +704,7 @@
 // ReplicaSetList is a collection of ReplicaSets.
 ReplicaSetList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -799,6 +809,7 @@
 // Deprecated: use PodSecurityPolicy from policy API Group instead.
 PodSecurityPolicy :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -1224,6 +1235,7 @@
 // Deprecated: use PodSecurityPolicyList from policy API Group instead.
 PodSecurityPolicyList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -1237,6 +1249,7 @@
 // NetworkPolicy describes what network traffic is allowed for a set of Pods
 NetworkPolicy :: {
 	metav1.TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -1409,6 +1422,7 @@
 // Network Policy List is a list of NetworkPolicy objects.
 NetworkPolicyList :: {
 	metav1.TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
diff --git a/doc/tutorial/kubernetes/quick/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/types_go_gen.cue b/doc/tutorial/kubernetes/quick/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/types_go_gen.cue
index 07bd4d8..9881cfa 100644
--- a/doc/tutorial/kubernetes/quick/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/types_go_gen.cue
+++ b/doc/tutorial/kubernetes/quick/pkg/k8s.io/apimachinery/pkg/apis/meta/v1/types_go_gen.cue
@@ -314,6 +314,7 @@
 // ListOptions is the query options to a standard REST list call.
 ListOptions :: {
 	TypeMeta
+
 	// A selector to restrict the list of returned objects by their labels.
 	// Defaults to everything.
 	// +optional
@@ -396,6 +397,7 @@
 // Deprecated. Planned for removal in 1.18.
 ExportOptions :: {
 	TypeMeta
+
 	// Should this value be exported.  Export strips fields that a user can not specify.
 	// Deprecated. Planned for removal in 1.18.
 	export: bool @go(Export) @protobuf(1,varint,opt)
@@ -408,6 +410,7 @@
 // GetOptions is the standard query options to the standard REST get call.
 GetOptions :: {
 	TypeMeta
+
 	// When specified:
 	// - if unset, then the result is returned from remote storage based on quorum-read flag;
 	// - if it's 0, then we simply return what we currently have in cache, no guarantee;
@@ -445,6 +448,7 @@
 // DeleteOptions may be provided when deleting an API object.
 DeleteOptions :: {
 	TypeMeta
+
 	// The duration in seconds before the object should be deleted. Value must be non-negative integer.
 	// The value zero indicates delete immediately. If this value is nil, the default grace period for the
 	// specified type will be used.
@@ -487,6 +491,7 @@
 // CreateOptions may be provided when creating an API object.
 CreateOptions :: {
 	TypeMeta
+
 	// When present, indicates that modifications should not be
 	// persisted. An invalid or unrecognized dryRun directive will
 	// result in an error response and no further processing of the
@@ -507,6 +512,7 @@
 // PatchOptions is meant to be a superset of UpdateOptions.
 PatchOptions :: {
 	TypeMeta
+
 	// When present, indicates that modifications should not be
 	// persisted. An invalid or unrecognized dryRun directive will
 	// result in an error response and no further processing of the
@@ -536,6 +542,7 @@
 // All fields in UpdateOptions should also be present in PatchOptions.
 UpdateOptions :: {
 	TypeMeta
+
 	// When present, indicates that modifications should not be
 	// persisted. An invalid or unrecognized dryRun directive will
 	// result in an error response and no further processing of the
@@ -566,6 +573,7 @@
 // Status is a return value for calls that don't return other objects.
 Status :: {
 	TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -886,6 +894,7 @@
 // List holds a list of objects, which may not be known by the server.
 List :: {
 	TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -902,6 +911,7 @@
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 APIVersions :: {
 	TypeMeta
+
 	// versions are the api versions that are available.
 	versions: [...string] @go(Versions,[]string) @protobuf(1,bytes,rep)
 
@@ -919,6 +929,7 @@
 // /apis.
 APIGroupList :: {
 	TypeMeta
+
 	// groups is a list of APIGroup.
 	groups: [...APIGroup] @go(Groups,[]APIGroup) @protobuf(1,bytes,rep)
 }
@@ -927,6 +938,7 @@
 // of a group.
 APIGroup :: {
 	TypeMeta
+
 	// name is the name of the group.
 	name: string @go(Name) @protobuf(1,bytes,opt)
 
@@ -1026,6 +1038,7 @@
 // is namespaced.
 APIResourceList :: {
 	TypeMeta
+
 	// groupVersion is the group and version this APIResourceList is for.
 	groupVersion: string @go(GroupVersion) @protobuf(1,bytes,opt)
 
@@ -1151,6 +1164,7 @@
 // +protobuf=false
 Table :: {
 	TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
@@ -1278,6 +1292,7 @@
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 TableOptions :: {
 	TypeMeta
+
 	// includeObject decides whether to include each object along with its columnar information.
 	// Specifying "None" will return no object, specifying "Object" will return the full object contents, and
 	// specifying "Metadata" (the default) will return the object's metadata in the PartialObjectMetadata kind
@@ -1290,6 +1305,7 @@
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 PartialObjectMetadata :: {
 	TypeMeta
+
 	// Standard object's metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata
 	// +optional
@@ -1300,6 +1316,7 @@
 // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
 PartialObjectMetadataList :: {
 	TypeMeta
+
 	// Standard list metadata.
 	// More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
 	// +optional
diff --git a/doc/tutorial/kubernetes/quick/services/kitchen/souschef/kube.cue b/doc/tutorial/kubernetes/quick/services/kitchen/souschef/kube.cue
index 11d9a94..e7c178f 100644
--- a/doc/tutorial/kubernetes/quick/services/kitchen/souschef/kube.cue
+++ b/doc/tutorial/kubernetes/quick/services/kitchen/souschef/kube.cue
@@ -4,4 +4,4 @@
 	image: "gcr.io/myproj/souschef:v0.5.3"
 }]
 
-deployment : souschef : spec : template : spec : hasDisks :: false
+deployment: souschef: spec: template: spec: hasDisks :: false