cue/format: replace bulk labels with ellipsis if possible
Change-Id: I1c46c97e82c993613f2b5731e956c1bf40b948a1
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/5080
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/cue/export.go b/cue/export.go
index cc79bd5..9abf05c 100644
--- a/cue/export.go
+++ b/cue/export.go
@@ -27,6 +27,7 @@
"cuelang.org/go/cue/ast"
"cuelang.org/go/cue/token"
+ "cuelang.org/go/internal"
)
func doEval(m options) bool {
@@ -218,6 +219,9 @@
func hasTemplate(s *ast.StructLit) bool {
for _, e := range s.Elts {
+ if _, ok := e.(*ast.Ellipsis); ok {
+ return true
+ }
if f, ok := e.(*ast.Field); ok {
label := f.Label
if _, ok := label.(*ast.TemplateLabel); ok {
@@ -692,7 +696,7 @@
return st
}
-func (p *exporter) optionals(st *ast.StructLit, x *optionals) {
+func (p *exporter) optionals(st *ast.StructLit, x *optionals) (skippedEllipsis bool) {
switch x.op {
default:
for _, t := range x.fields {
@@ -705,10 +709,15 @@
if c, ok := v.(*closeIfStruct); ok {
v = c.value
}
- st.Elts = append(st.Elts, &ast.Field{
+ f := &ast.Field{
Label: p.mkTemplate(t.key, p.identifier(l.params.arcs[0].feature)),
Value: p.expr(l.value),
- })
+ }
+ if internal.IsEllipsis(f) {
+ skippedEllipsis = true
+ continue
+ }
+ st.Elts = append(st.Elts, f)
}
case opUnify:
@@ -731,6 +740,7 @@
st.Elts = append(st.Elts, &ast.EmbedDecl{Expr: left})
st.Elts = append(st.Elts, &ast.EmbedDecl{Expr: right})
}
+ return skippedEllipsis
}
func (p *exporter) structure(x *structLit, addTempl bool) (ret *ast.StructLit, err *bottom) {
@@ -753,11 +763,12 @@
if x.emit != nil {
obj.Elts = append(obj.Elts, &ast.EmbedDecl{Expr: p.expr(x.emit)})
}
+ hasEllipsis := false
if p.showOptional() && x.optionals != nil &&
// Optional field constraints may be omitted if they were already
// applied and no more new fields may be added.
!(doEval(p.mode) && x.optionals.isEmpty() && p.isClosed(x)) {
- p.optionals(obj, x.optionals)
+ hasEllipsis = p.optionals(obj, x.optionals)
}
for i, a := range x.arcs {
f := &ast.Field{
@@ -839,6 +850,10 @@
}
}
}
+
+ if hasEllipsis {
+ obj.Elts = append(obj.Elts, &ast.Ellipsis{})
+ }
return obj, nil
}
diff --git a/cue/export_test.go b/cue/export_test.go
index 4771064..51f5202 100644
--- a/cue/export_test.go
+++ b/cue/export_test.go
@@ -300,15 +300,15 @@
eval: true,
noOpt: true,
in: `{
- job <Name>: {
+ job: [Name=_]: {
name: Name
replicas: uint | *1 @protobuf(10)
command: string
}
-
- job list command: "ls"
- job nginx: {
+ job: list: command: "ls"
+
+ job: nginx: {
command: "nginx"
replicas: 2
}
@@ -555,6 +555,16 @@
A: [>=0]
B: [10] | [192]
}`),
+ }, {
+ in: `{
+ [string]: _
+ foo: 3
+ }`,
+ out: unindent(`
+ {
+ foo: 3
+ ...
+ }`),
}}
for _, tc := range testCases {
t.Run("", func(t *testing.T) {
diff --git a/cue/format/node.go b/cue/format/node.go
index 3972a49..b6fdda5 100644
--- a/cue/format/node.go
+++ b/cue/format/node.go
@@ -23,6 +23,7 @@
"cuelang.org/go/cue/literal"
"cuelang.org/go/cue/scanner"
"cuelang.org/go/cue/token"
+ "cuelang.org/go/internal"
)
func printNode(node interface{}, f *printer) error {
@@ -103,6 +104,7 @@
func (f *formatter) walkDeclList(list []ast.Decl) {
f.before(nil)
d := 0
+ hasEllipsis := false
for i, x := range list {
if i > 0 {
f.print(declcomma)
@@ -125,6 +127,10 @@
}
}
}
+ if f.printer.cfg.simplify && internal.IsEllipsis(x) {
+ hasEllipsis = true
+ continue
+ }
f.decl(x)
d = 0
if f, ok := x.(*ast.Field); ok {
@@ -150,6 +156,10 @@
}
f.print(f.current.parentSep)
}
+ if hasEllipsis {
+ f.decl(&ast.Ellipsis{})
+ f.print(f.current.parentSep)
+ }
f.after(nil)
}
diff --git a/cue/format/testdata/simplify.golden b/cue/format/testdata/simplify.golden
index 84506b9..17c024e 100644
--- a/cue/format/testdata/simplify.golden
+++ b/cue/format/testdata/simplify.golden
@@ -30,6 +30,11 @@
// Issue #294
"\("x")": "x"
+a :: {
+ foo: 2
+ ...
+}
+
x: {
@tag0(foo)
r1: baz1
diff --git a/cue/format/testdata/simplify.input b/cue/format/testdata/simplify.input
index 271c6a0..8b14541 100644
--- a/cue/format/testdata/simplify.input
+++ b/cue/format/testdata/simplify.input
@@ -32,6 +32,11 @@
// Issue #294
"\("x")": "x"
+a :: {
+ [string]: _
+ foo: 2
+}
+
x: {
@tag0(foo)
r1: baz1
diff --git a/internal/internal.go b/internal/internal.go
index 67a4e71..16f1e44 100644
--- a/internal/internal.go
+++ b/internal/internal.go
@@ -126,3 +126,30 @@
}
return cg
}
+
+// IsEllipsis reports whether the declaration can be represented as an ellipsis.
+func IsEllipsis(x ast.Decl) bool {
+ // ...
+ if _, ok := x.(*ast.Ellipsis); ok {
+ return true
+ }
+
+ // [string]: _ or [_]: _
+ f, ok := x.(*ast.Field)
+ if !ok {
+ return false
+ }
+ v, ok := f.Value.(*ast.Ident)
+ if !ok || v.Name != "_" {
+ return false
+ }
+ l, ok := f.Label.(*ast.ListLit)
+ if !ok || len(l.Elts) != 1 {
+ return false
+ }
+ i, ok := l.Elts[0].(*ast.Ident)
+ if !ok {
+ return false
+ }
+ return i.Name == "string" || i.Name == "_"
+}