blob: 22d901aa902b09f320fc3e8f2e4a2ff7aad80acc [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) {
Marcel van Lohuizen18736bf2020-07-15 17:02:42 +020032 return extractDocs(v, v.Conjuncts)
33}
34
35func extractDocs(v *adt.Vertex, a []adt.Conjunct) (docs []*ast.CommentGroup) {
Marcel van Lohuizen0d12c332020-06-10 12:36:58 +020036 fields := []*ast.Field{}
37
38 // Collect docs directly related to this Vertex.
Marcel van Lohuizen18736bf2020-07-15 17:02:42 +020039 for _, x := range a {
Marcel van Lohuizen0d12c332020-06-10 12:36:58 +020040 f, ok := x.Source().(*ast.Field)
41 if !ok || hasShorthandValue(f) {
42 continue
43 }
44
45 fields = append(fields, f)
46 for _, cg := range f.Comments() {
47 if !containsDoc(docs, cg) && cg.Doc {
48 docs = append(docs, cg)
49 }
50 }
51 }
52
Marcel van Lohuizen18736bf2020-07-15 17:02:42 +020053 if v == nil {
54 return docs
55 }
56
Marcel van Lohuizen0d12c332020-06-10 12:36:58 +020057 // Collect docs from parent scopes in collapsed fields.
58 for p := v.Parent; p != nil; p = p.Parent {
59
60 newFields := []*ast.Field{}
61
62 for _, x := range p.Conjuncts {
63 f, ok := x.Source().(*ast.Field)
64 if !ok || !hasShorthandValue(f) {
65 continue
66 }
67
68 nested := nestedField(f)
69 for _, child := range fields {
70 if nested == child {
71 newFields = append(newFields, f)
72 for _, cg := range f.Comments() {
73 if !containsDoc(docs, cg) && cg.Doc {
74 docs = append(docs, cg)
75 }
76 }
77 }
78 }
79 }
80
81 fields = newFields
82 }
83 return docs
84}
85
86// hasShorthandValue reports whether this field has a struct value that will
87// be rendered as a shorthand, for instance:
88//
89// f: g: 2
90//
91func hasShorthandValue(f *ast.Field) bool {
92 if f = nestedField(f); f == nil {
93 return false
94 }
95
96 // Not a regular field, but shorthand field.
97 // TODO: Should we return here? For now mimic old implementation.
98 if _, _, err := ast.LabelName(f.Label); err != nil {
99 return false
100 }
101
102 return true
103}
104
105// nestedField returns the child field of a field shorthand.
106func nestedField(f *ast.Field) *ast.Field {
107 s, _ := f.Value.(*ast.StructLit)
108 if s == nil ||
109 len(s.Elts) != 1 ||
110 s.Lbrace != token.NoPos ||
111 s.Rbrace != token.NoPos {
112 return nil
113 }
114
115 f, _ = s.Elts[0].(*ast.Field)
116 return f
117}
118
119func containsDoc(a []*ast.CommentGroup, cg *ast.CommentGroup) bool {
120 for _, c := range a {
121 if c == cg {
122 return true
123 }
124 }
125
126 for _, c := range a {
127 if c.Text() == cg.Text() {
128 return true
129 }
130 }
131
132 return false
133}
134
135func ExtractFieldAttrs(a []adt.Conjunct) (attrs []*ast.Attribute) {
136 for _, x := range a {
137 f, ok := x.Source().(*ast.Field)
138 if !ok {
139 continue
140 }
141 for _, a := range f.Attrs {
142 if !containsAttr(attrs, a) {
143 attrs = append(attrs, a)
144 }
145 }
146 }
147 return attrs
148}
149
150func containsAttr(a []*ast.Attribute, x *ast.Attribute) bool {
151 for _, e := range a {
152 if e.Text == x.Text {
153 return true
154 }
155 }
156 return false
157}