internal/core/export: initial commit
Change-Id: Ib388135cc653bdb21d8bc72bea0983df429ec9c2
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/6504
Reviewed-by: CUE cueckoo <cueckoo@gmail.com>
Reviewed-by: Marcel van Lohuizen <mpvl@golang.org>
diff --git a/internal/core/export/extract.go b/internal/core/export/extract.go
new file mode 100644
index 0000000..c3b11ef
--- /dev/null
+++ b/internal/core/export/extract.go
@@ -0,0 +1,149 @@
+// Copyright 2020 CUE Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package export
+
+import (
+ "cuelang.org/go/cue/ast"
+ "cuelang.org/go/cue/token"
+ "cuelang.org/go/internal/core/adt"
+)
+
+// ExtractDoc collects documentation strings for a field.
+//
+// Comments are attached to a field with a field shorthand belong to the
+// child node. So in the following the comment is attached to field bar.
+//
+// // comment
+// foo: bar: 2
+//
+func ExtractDoc(v *adt.Vertex) (docs []*ast.CommentGroup) {
+ fields := []*ast.Field{}
+
+ // Collect docs directly related to this Vertex.
+ for _, x := range v.Conjuncts {
+ f, ok := x.Source().(*ast.Field)
+ if !ok || hasShorthandValue(f) {
+ continue
+ }
+
+ fields = append(fields, f)
+ for _, cg := range f.Comments() {
+ if !containsDoc(docs, cg) && cg.Doc {
+ docs = append(docs, cg)
+ }
+ }
+ }
+
+ // Collect docs from parent scopes in collapsed fields.
+ for p := v.Parent; p != nil; p = p.Parent {
+
+ newFields := []*ast.Field{}
+
+ for _, x := range p.Conjuncts {
+ f, ok := x.Source().(*ast.Field)
+ if !ok || !hasShorthandValue(f) {
+ continue
+ }
+
+ nested := nestedField(f)
+ for _, child := range fields {
+ if nested == child {
+ newFields = append(newFields, f)
+ for _, cg := range f.Comments() {
+ if !containsDoc(docs, cg) && cg.Doc {
+ docs = append(docs, cg)
+ }
+ }
+ }
+ }
+ }
+
+ fields = newFields
+ }
+ return docs
+}
+
+// hasShorthandValue reports whether this field has a struct value that will
+// be rendered as a shorthand, for instance:
+//
+// f: g: 2
+//
+func hasShorthandValue(f *ast.Field) bool {
+ if f = nestedField(f); f == nil {
+ return false
+ }
+
+ // Not a regular field, but shorthand field.
+ // TODO: Should we return here? For now mimic old implementation.
+ if _, _, err := ast.LabelName(f.Label); err != nil {
+ return false
+ }
+
+ return true
+}
+
+// nestedField returns the child field of a field shorthand.
+func nestedField(f *ast.Field) *ast.Field {
+ s, _ := f.Value.(*ast.StructLit)
+ if s == nil ||
+ len(s.Elts) != 1 ||
+ s.Lbrace != token.NoPos ||
+ s.Rbrace != token.NoPos {
+ return nil
+ }
+
+ f, _ = s.Elts[0].(*ast.Field)
+ return f
+}
+
+func containsDoc(a []*ast.CommentGroup, cg *ast.CommentGroup) bool {
+ for _, c := range a {
+ if c == cg {
+ return true
+ }
+ }
+
+ for _, c := range a {
+ if c.Text() == cg.Text() {
+ return true
+ }
+ }
+
+ return false
+}
+
+func ExtractFieldAttrs(a []adt.Conjunct) (attrs []*ast.Attribute) {
+ for _, x := range a {
+ f, ok := x.Source().(*ast.Field)
+ if !ok {
+ continue
+ }
+ for _, a := range f.Attrs {
+ if !containsAttr(attrs, a) {
+ attrs = append(attrs, a)
+ }
+ }
+ }
+ return attrs
+}
+
+func containsAttr(a []*ast.Attribute, x *ast.Attribute) bool {
+ for _, e := range a {
+ if e.Text == x.Text {
+ return true
+ }
+ }
+ return false
+}