@@ -10,8 +10,8 @@ import (
10
10
"github.com/docker/docker-language-server/internal/pkg/document"
11
11
"github.com/docker/docker-language-server/internal/tliron/glsp/protocol"
12
12
"github.com/docker/docker-language-server/internal/types"
13
+ "github.com/goccy/go-yaml/ast"
13
14
"github.com/santhosh-tekuri/jsonschema/v6"
14
- "gopkg.in/yaml.v3"
15
15
)
16
16
17
17
func Completion (ctx context.Context , params * protocol.CompletionParams , doc document.ComposeDocument ) (* protocol.CompletionList , error ) {
@@ -32,38 +32,26 @@ func Completion(ctx context.Context, params *protocol.CompletionParams, doc docu
32
32
33
33
lines := strings .Split (string (doc .Input ()), "\n " )
34
34
lspLine := int (params .Position .Line )
35
- if lspLine >= len (lines ) {
36
- return nil , nil
37
- }
38
-
39
35
if strings .HasPrefix (strings .TrimSpace (lines [lspLine ]), "#" ) {
40
36
return nil , nil
41
37
}
42
38
43
- root := doc .RootNode ()
44
- if len (root . Content ) == 0 {
39
+ file := doc .File ()
40
+ if file == nil || len (file . Docs ) == 0 {
45
41
return nil , nil
46
42
}
47
43
48
44
line := int (lspLine ) + 1
49
45
character := int (params .Position .Character ) + 1
50
- topLevel , _ , _ := NodeStructure ( line , root . Content [ 0 ]. Content )
51
- if len (topLevel ) == 0 {
46
+ path := constructCompletionNodePath ( file , line )
47
+ if len (path ) == 1 {
52
48
return nil , nil
53
- } else if len (topLevel ) == 1 {
54
- return nil , nil
55
- } else if topLevel [1 ].Column >= character {
56
- return nil , nil
57
- } else if len (topLevel ) > 2 && topLevel [1 ].Column < character && character < topLevel [2 ].Column {
58
- topLevel = []* yaml.Node {topLevel [0 ], topLevel [1 ]}
59
- }
60
-
61
- if topLevel [0 ].Line == line {
49
+ } else if path [1 ].Key .GetToken ().Position .Column >= character {
62
50
return nil , nil
63
51
}
64
52
65
53
items := []protocol.CompletionItem {}
66
- nodeProps := nodeProperties (topLevel , line , character )
54
+ nodeProps := nodeProperties (path , line , character )
67
55
if schema , ok := nodeProps .(* jsonschema.Schema ); ok {
68
56
if schema .Enum != nil {
69
57
for _ , value := range schema .Enum .Values {
@@ -121,64 +109,87 @@ func Completion(ctx context.Context, params *protocol.CompletionParams, doc docu
121
109
return & protocol.CompletionList {Items : items }, nil
122
110
}
123
111
124
- func NodeStructure (line int , rootNodes []* yaml.Node ) ([]* yaml.Node , * yaml.Node , bool ) {
112
+ func constructCompletionNodePath (file * ast.File , line int ) []* ast.MappingValueNode {
113
+ for _ , documentNode := range file .Docs {
114
+ if mappingNode , ok := documentNode .Body .(* ast.MappingNode ); ok {
115
+ return NodeStructure (line , mappingNode .Values )
116
+ }
117
+ }
118
+ return nil
119
+ }
120
+
121
+ func NodeStructure (line int , rootNodes []* ast.MappingValueNode ) []* ast.MappingValueNode {
125
122
if len (rootNodes ) == 0 {
126
- return nil , nil , false
127
- }
128
-
129
- var topLevel * yaml.Node
130
- var content * yaml.Node
131
- for i := 0 ; i < len (rootNodes ); i += 2 {
132
- if rootNodes [i ].Line < line {
133
- topLevel = rootNodes [i ]
134
- content = rootNodes [i + 1 ]
135
- } else if rootNodes [i ].Line == line {
136
- return []* yaml.Node {rootNodes [i ]}, rootNodes [i + 1 ], true
137
- } else if line < rootNodes [i ].Line {
123
+ return nil
124
+ }
125
+
126
+ var candidate * ast.MappingValueNode
127
+ for _ , node := range rootNodes {
128
+ if node .GetToken ().Position .Line < line {
129
+ candidate = node
130
+ } else if node .GetToken ().Position .Line == line {
131
+ return []* ast.MappingValueNode {node }
132
+ } else {
138
133
break
139
134
}
140
135
}
141
- nodes := []* yaml. Node { topLevel }
142
- candidates , subcontent := walkNodes (line , content . Content )
136
+ nodes := []* ast. MappingValueNode { candidate }
137
+ candidates := walkNodes (line , candidate )
143
138
nodes = append (nodes , candidates ... )
144
- if subcontent != nil {
145
- content = subcontent
146
- }
147
- return nodes , content , false
139
+ return nodes
148
140
}
149
141
150
- func walkNodes (line int , nodes []* yaml.Node ) ([]* yaml.Node , * yaml.Node ) {
151
- var candidate * yaml.Node
152
- var candidateContent * yaml.Node
153
- for i := 0 ; i < len (nodes ); i += 2 {
154
- if nodes [i ].Line < line {
155
- candidate = nodes [i ]
156
- if candidate .Kind == yaml .MappingNode {
157
- return walkNodes (line , candidate .Content )
142
+ func walkNodes (line int , node * ast.MappingValueNode ) []* ast.MappingValueNode {
143
+ var candidate ast.Node
144
+ value := node .Value
145
+ if mappingNode , ok := value .(* ast.MappingNode ); ok {
146
+ for _ , child := range mappingNode .Values {
147
+ if child .GetToken ().Position .Line < line {
148
+ candidate = child
149
+ } else if child .GetToken ().Position .Line == line {
150
+ candidate = child
151
+ break
158
152
}
159
- if len (nodes ) == i + 1 {
160
- return []* yaml.Node {candidate }, nil
153
+ }
154
+ } else if sequenceNode , ok := value .(* ast.SequenceNode ); ok {
155
+ for _ , child := range sequenceNode .Values {
156
+ if child .GetToken ().Position .Line < line {
157
+ if _ , ok := child .(* ast.NullNode ); ok {
158
+ continue
159
+ }
160
+ candidate = child
161
+ } else if child .GetToken ().Position .Line == line {
162
+ if _ , ok := child .(* ast.NullNode ); ok {
163
+ break
164
+ }
165
+ candidate = child
166
+ break
161
167
}
162
- candidateContent = nodes [i + 1 ]
163
- } else if nodes [i ].Line == line {
164
- if nodes [i ].Kind == yaml .MappingNode {
165
- return walkNodes (line , nodes [i ].Content )
168
+ }
169
+ }
170
+
171
+ if mappingNode , ok := candidate .(* ast.MappingNode ); ok {
172
+ for _ , child := range mappingNode .Values {
173
+ if child .GetToken ().Position .Line < line {
174
+ candidate = child
175
+ } else if child .GetToken ().Position .Line == line {
176
+ candidate = child
177
+ break
166
178
}
167
- return []* yaml.Node {nodes [i ]}, nil
168
- } else if line < nodes [i ].Line {
169
- break
170
179
}
171
180
}
172
- if candidateContent == nil {
173
- return []* yaml.Node {}, nil
181
+
182
+ if candidate == nil {
183
+ return []* ast.MappingValueNode {}
174
184
}
175
- walked , subcontent := walkNodes (line , candidateContent .Content )
176
- candidates := []* yaml.Node {candidate }
177
- candidates = append (candidates , walked ... )
178
- if subcontent != nil {
179
- candidateContent = subcontent
185
+
186
+ if next , ok := candidate .(* ast.MappingValueNode ); ok {
187
+ nodes := []* ast.MappingValueNode {next }
188
+ candidates := walkNodes (line , next )
189
+ nodes = append (nodes , candidates ... )
190
+ return nodes
180
191
}
181
- return candidates , candidateContent
192
+ return [] * ast. MappingValueNode {}
182
193
}
183
194
184
195
func extractDetail (schema * jsonschema.Schema ) * string {
0 commit comments