blob: 792563cb168e87a41838e4070080edfc4cd5bb55 [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"
Marcel van Lohuizen34587b92021-01-27 16:54:03 +010020 "cuelang.org/go/internal"
Marcel van Lohuizen0d12c332020-06-10 12:36:58 +020021 "cuelang.org/go/internal/core/adt"
22)
23
24// ExtractDoc collects documentation strings for a field.
25//
26// Comments are attached to a field with a field shorthand belong to the
27// child node. So in the following the comment is attached to field bar.
28//
29// // comment
30// foo: bar: 2
31//
32func ExtractDoc(v *adt.Vertex) (docs []*ast.CommentGroup) {
Marcel van Lohuizen18736bf2020-07-15 17:02:42 +020033 return extractDocs(v, v.Conjuncts)
34}
35
36func extractDocs(v *adt.Vertex, a []adt.Conjunct) (docs []*ast.CommentGroup) {
Marcel van Lohuizen0d12c332020-06-10 12:36:58 +020037 fields := []*ast.Field{}
38
39 // Collect docs directly related to this Vertex.
Marcel van Lohuizen18736bf2020-07-15 17:02:42 +020040 for _, x := range a {
Marcel van Lohuizend4c600f2021-01-07 11:17:13 +010041 if v, ok := x.Expr().(*adt.Vertex); ok {
42 docs = append(docs, extractDocs(v, v.Conjuncts)...)
43 continue
44 }
Marcel van Lohuizen0d12c332020-06-10 12:36:58 +020045 f, ok := x.Source().(*ast.Field)
46 if !ok || hasShorthandValue(f) {
47 continue
48 }
49
50 fields = append(fields, f)
51 for _, cg := range f.Comments() {
52 if !containsDoc(docs, cg) && cg.Doc {
53 docs = append(docs, cg)
54 }
55 }
56 }
57
Marcel van Lohuizen18736bf2020-07-15 17:02:42 +020058 if v == nil {
59 return docs
60 }
61
Marcel van Lohuizen0d12c332020-06-10 12:36:58 +020062 // Collect docs from parent scopes in collapsed fields.
63 for p := v.Parent; p != nil; p = p.Parent {
64
65 newFields := []*ast.Field{}
66
67 for _, x := range p.Conjuncts {
68 f, ok := x.Source().(*ast.Field)
69 if !ok || !hasShorthandValue(f) {
70 continue
71 }
72
73 nested := nestedField(f)
74 for _, child := range fields {
75 if nested == child {
76 newFields = append(newFields, f)
77 for _, cg := range f.Comments() {
78 if !containsDoc(docs, cg) && cg.Doc {
79 docs = append(docs, cg)
80 }
81 }
82 }
83 }
84 }
85
86 fields = newFields
87 }
88 return docs
89}
90
91// hasShorthandValue reports whether this field has a struct value that will
92// be rendered as a shorthand, for instance:
93//
94// f: g: 2
95//
96func hasShorthandValue(f *ast.Field) bool {
97 if f = nestedField(f); f == nil {
98 return false
99 }
100
101 // Not a regular field, but shorthand field.
102 // TODO: Should we return here? For now mimic old implementation.
103 if _, _, err := ast.LabelName(f.Label); err != nil {
104 return false
105 }
106
107 return true
108}
109
110// nestedField returns the child field of a field shorthand.
111func nestedField(f *ast.Field) *ast.Field {
112 s, _ := f.Value.(*ast.StructLit)
113 if s == nil ||
114 len(s.Elts) != 1 ||
115 s.Lbrace != token.NoPos ||
116 s.Rbrace != token.NoPos {
117 return nil
118 }
119
120 f, _ = s.Elts[0].(*ast.Field)
121 return f
122}
123
124func containsDoc(a []*ast.CommentGroup, cg *ast.CommentGroup) bool {
125 for _, c := range a {
126 if c == cg {
127 return true
128 }
129 }
130
131 for _, c := range a {
132 if c.Text() == cg.Text() {
133 return true
134 }
135 }
136
137 return false
138}
139
Marcel van Lohuizenf1d3d812021-04-11 15:29:33 +0200140func ExtractFieldAttrs(v *adt.Vertex) (attrs []*ast.Attribute) {
141 for _, x := range v.Conjuncts {
142 attrs = extractFieldAttrs(attrs, x)
143 }
144 return attrs
145}
146
147func extractFieldAttrs(attrs []*ast.Attribute, c adt.Conjunct) []*ast.Attribute {
148 if f, ok := c.Source().(*ast.Field); ok {
Marcel van Lohuizen0d12c332020-06-10 12:36:58 +0200149 for _, a := range f.Attrs {
150 if !containsAttr(attrs, a) {
151 attrs = append(attrs, a)
152 }
153 }
154 }
155 return attrs
156}
157
Marcel van Lohuizenf1d3d812021-04-11 15:29:33 +0200158func ExtractDeclAttrs(v *adt.Vertex) (attrs []*ast.Attribute) {
159 for _, st := range v.Structs {
160 if src := st.StructLit; src != nil {
161 attrs = extractDeclAttrs(attrs, src.Src)
162 }
Marcel van Lohuizen34587b92021-01-27 16:54:03 +0100163 }
164 return attrs
165}
166
167func extractDeclAttrs(attrs []*ast.Attribute, n ast.Node) []*ast.Attribute {
168 switch x := n.(type) {
169 case nil:
170 case *ast.File:
171 info := internal.GetPackageInfo(x)
172 attrs = appendDeclAttrs(attrs, x.Decls[info.Index:])
173 case *ast.StructLit:
174 attrs = appendDeclAttrs(attrs, x.Elts)
175 }
176 return attrs
177}
178
179func appendDeclAttrs(a []*ast.Attribute, decls []ast.Decl) []*ast.Attribute {
180 for _, d := range decls {
181 if attr, ok := d.(*ast.Attribute); ok && !containsAttr(a, attr) {
182 a = append(a, attr)
183 }
184 }
185 return a
186}
187
Marcel van Lohuizen0d12c332020-06-10 12:36:58 +0200188func containsAttr(a []*ast.Attribute, x *ast.Attribute) bool {
189 for _, e := range a {
190 if e.Text == x.Text {
191 return true
192 }
193 }
194 return false
195}