blob: c3b11efebade7eab1a1afc8ff3d8e161468a8ec2 [file] [log] [blame]
Marcel van Lohuizen0d12c332020-06-10 12:36:58 +02001// Copyright 2020 CUE Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package export
16
17import (
18 "cuelang.org/go/cue/ast"
19 "cuelang.org/go/cue/token"
20 "cuelang.org/go/internal/core/adt"
21)
22
23// ExtractDoc collects documentation strings for a field.
24//
25// Comments are attached to a field with a field shorthand belong to the
26// child node. So in the following the comment is attached to field bar.
27//
28// // comment
29// foo: bar: 2
30//
31func ExtractDoc(v *adt.Vertex) (docs []*ast.CommentGroup) {
32 fields := []*ast.Field{}
33
34 // Collect docs directly related to this Vertex.
35 for _, x := range v.Conjuncts {
36 f, ok := x.Source().(*ast.Field)
37 if !ok || hasShorthandValue(f) {
38 continue
39 }
40
41 fields = append(fields, f)
42 for _, cg := range f.Comments() {
43 if !containsDoc(docs, cg) && cg.Doc {
44 docs = append(docs, cg)
45 }
46 }
47 }
48
49 // Collect docs from parent scopes in collapsed fields.
50 for p := v.Parent; p != nil; p = p.Parent {
51
52 newFields := []*ast.Field{}
53
54 for _, x := range p.Conjuncts {
55 f, ok := x.Source().(*ast.Field)
56 if !ok || !hasShorthandValue(f) {
57 continue
58 }
59
60 nested := nestedField(f)
61 for _, child := range fields {
62 if nested == child {
63 newFields = append(newFields, f)
64 for _, cg := range f.Comments() {
65 if !containsDoc(docs, cg) && cg.Doc {
66 docs = append(docs, cg)
67 }
68 }
69 }
70 }
71 }
72
73 fields = newFields
74 }
75 return docs
76}
77
78// hasShorthandValue reports whether this field has a struct value that will
79// be rendered as a shorthand, for instance:
80//
81// f: g: 2
82//
83func hasShorthandValue(f *ast.Field) bool {
84 if f = nestedField(f); f == nil {
85 return false
86 }
87
88 // Not a regular field, but shorthand field.
89 // TODO: Should we return here? For now mimic old implementation.
90 if _, _, err := ast.LabelName(f.Label); err != nil {
91 return false
92 }
93
94 return true
95}
96
97// nestedField returns the child field of a field shorthand.
98func nestedField(f *ast.Field) *ast.Field {
99 s, _ := f.Value.(*ast.StructLit)
100 if s == nil ||
101 len(s.Elts) != 1 ||
102 s.Lbrace != token.NoPos ||
103 s.Rbrace != token.NoPos {
104 return nil
105 }
106
107 f, _ = s.Elts[0].(*ast.Field)
108 return f
109}
110
111func containsDoc(a []*ast.CommentGroup, cg *ast.CommentGroup) bool {
112 for _, c := range a {
113 if c == cg {
114 return true
115 }
116 }
117
118 for _, c := range a {
119 if c.Text() == cg.Text() {
120 return true
121 }
122 }
123
124 return false
125}
126
127func ExtractFieldAttrs(a []adt.Conjunct) (attrs []*ast.Attribute) {
128 for _, x := range a {
129 f, ok := x.Source().(*ast.Field)
130 if !ok {
131 continue
132 }
133 for _, a := range f.Attrs {
134 if !containsAttr(attrs, a) {
135 attrs = append(attrs, a)
136 }
137 }
138 }
139 return attrs
140}
141
142func containsAttr(a []*ast.Attribute, x *ast.Attribute) bool {
143 for _, e := range a {
144 if e.Text == x.Text {
145 return true
146 }
147 }
148 return false
149}