4
4
"context"
5
5
"strings"
6
6
7
+ "github.com/google/go-jsonnet"
7
8
"github.com/google/go-jsonnet/ast"
9
+ "github.com/google/go-jsonnet/toolutils"
8
10
"github.com/grafana/jsonnet-language-server/pkg/ast/processing"
9
11
"github.com/grafana/jsonnet-language-server/pkg/nodestack"
10
12
position "github.com/grafana/jsonnet-language-server/pkg/position_conversion"
@@ -38,7 +40,9 @@ func (s *Server) Completion(ctx context.Context, params *protocol.CompletionPara
38
40
return nil , nil
39
41
}
40
42
41
- items := s .completionFromStack (line , searchStack )
43
+ vm := s .getVM (doc .item .URI .SpanURI ().Filename ())
44
+
45
+ items := s .completionFromStack (line , searchStack , vm )
42
46
return & protocol.CompletionList {IsIncomplete : false , Items : items }, nil
43
47
}
44
48
@@ -52,43 +56,15 @@ func getCompletionLine(fileContent string, position protocol.Position) string {
52
56
return line
53
57
}
54
58
55
- func (s * Server ) completionFromStack (line string , stack * nodestack.NodeStack ) []protocol.CompletionItem {
56
- var items []protocol.CompletionItem
57
-
59
+ func (s * Server ) completionFromStack (line string , stack * nodestack.NodeStack , vm * jsonnet.VM ) []protocol.CompletionItem {
58
60
lineWords := strings .Split (line , " " )
59
61
lastWord := lineWords [len (lineWords )- 1 ]
60
62
61
63
indexes := strings .Split (lastWord , "." )
62
64
firstIndex , indexes := indexes [0 ], indexes [1 :]
63
65
64
- if firstIndex == "self" && len (indexes ) > 0 {
65
- fieldPrefix := indexes [0 ]
66
-
67
- for ! stack .IsEmpty () {
68
- curr := stack .Pop ()
69
-
70
- switch curr := curr .(type ) {
71
- case * ast.Binary :
72
- stack .Push (curr .Left )
73
- stack .Push (curr .Right )
74
- case * ast.DesugaredObject :
75
- for _ , field := range curr .Fields {
76
- label := processing .FieldNameToString (field .Name )
77
- // Ignore fields that don't match the prefix
78
- if ! strings .HasPrefix (label , fieldPrefix ) {
79
- continue
80
- }
81
-
82
- // Ignore the current field
83
- if strings .Contains (line , label + ":" ) {
84
- continue
85
- }
86
-
87
- items = append (items , createCompletionItem (label , "self." + label , protocol .FieldCompletion , field .Body ))
88
- }
89
- }
90
- }
91
- } else if len (indexes ) == 0 {
66
+ if len (indexes ) == 0 {
67
+ var items []protocol.CompletionItem
92
68
// firstIndex is a variable (local) completion
93
69
for ! stack .IsEmpty () {
94
70
if curr , ok := stack .Pop ().(* ast.Local ); ok {
@@ -103,13 +79,51 @@ func (s *Server) completionFromStack(line string, stack *nodestack.NodeStack) []
103
79
}
104
80
}
105
81
}
82
+ return items
106
83
}
107
84
108
- return items
85
+ if len (indexes ) > 1 {
86
+ // TODO: Support multiple indexes, the objects to search through will be the reference in the last index
87
+ return nil
88
+ }
89
+
90
+ var (
91
+ objectsToSearch []* ast.DesugaredObject
92
+ )
93
+
94
+ if firstIndex == "self" {
95
+ // Search through the current stack
96
+ objectsToSearch = processing .FindTopLevelObjects (stack , vm )
97
+ } else {
98
+ // If the index is something other than 'self', find what it refers to (Var reference) and find objects in that
99
+ for ! stack .IsEmpty () {
100
+ curr := stack .Pop ()
101
+
102
+ if targetVar , ok := curr .(* ast.Var ); ok && string (targetVar .Id ) == firstIndex {
103
+ ref , _ := processing .FindVarReference (targetVar , vm )
104
+
105
+ switch ref := ref .(type ) {
106
+ case * ast.DesugaredObject :
107
+ objectsToSearch = []* ast.DesugaredObject {ref }
108
+ case * ast.Import :
109
+ filename := ref .File .Value
110
+ objectsToSearch = processing .FindTopLevelObjectsInFile (vm , filename , string (curr .Loc ().File .DiagnosticFileName ))
111
+ }
112
+ break
113
+ }
114
+
115
+ for _ , node := range toolutils .Children (curr ) {
116
+ stack .Push (node )
117
+ }
118
+ }
119
+ }
120
+
121
+ fieldPrefix := indexes [0 ]
122
+ return createCompletionItemsFromObjects (objectsToSearch , firstIndex , fieldPrefix , line )
109
123
}
110
124
111
125
func (s * Server ) completionStdLib (line string ) []protocol.CompletionItem {
112
- items := []protocol.CompletionItem {}
126
+ var items []protocol.CompletionItem
113
127
114
128
stdIndex := strings .LastIndex (line , "std." )
115
129
if stdIndex != - 1 {
@@ -147,6 +161,36 @@ func (s *Server) completionStdLib(line string) []protocol.CompletionItem {
147
161
return items
148
162
}
149
163
164
+ func createCompletionItemsFromObjects (objects []* ast.DesugaredObject , firstIndex , fieldPrefix , currentLine string ) []protocol.CompletionItem {
165
+ var items []protocol.CompletionItem
166
+ labels := make (map [string ]bool )
167
+
168
+ for _ , obj := range objects {
169
+ for _ , field := range obj .Fields {
170
+ label := processing .FieldNameToString (field .Name )
171
+
172
+ if labels [label ] {
173
+ continue
174
+ }
175
+
176
+ // Ignore fields that don't match the prefix
177
+ if ! strings .HasPrefix (label , fieldPrefix ) {
178
+ continue
179
+ }
180
+
181
+ // Ignore the current field
182
+ if strings .Contains (currentLine , label + ":" ) {
183
+ continue
184
+ }
185
+
186
+ items = append (items , createCompletionItem (label , firstIndex + "." + label , protocol .FieldCompletion , field .Body ))
187
+ labels [label ] = true
188
+ }
189
+ }
190
+
191
+ return items
192
+ }
193
+
150
194
func createCompletionItem (label , detail string , kind protocol.CompletionItemKind , body ast.Node ) protocol.CompletionItem {
151
195
insertText := label
152
196
if asFunc , ok := body .(* ast.Function ); ok {
0 commit comments