Skip to content

Commit 051611d

Browse files
authored
Merge pull request #120 from docker/refactor-yaml-definition-links
Begin refactoring go-yaml/yaml to goccy/go-yaml
2 parents ced8cf5 + 3a24707 commit 051611d

File tree

7 files changed

+445
-147
lines changed

7 files changed

+445
-147
lines changed

go.mod

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/bep/debounce v1.2.1
77
github.com/docker/buildx v0.23.0
88
github.com/go-git/go-git/v5 v5.14.0
9+
github.com/goccy/go-yaml v1.11.3
910
github.com/hashicorp/hcl-lang v0.0.0-20250210193002-b2ec3be7c1b8
1011
github.com/hashicorp/hcl/v2 v2.23.0
1112
github.com/moby/buildkit v0.21.0
@@ -69,6 +70,7 @@ require (
6970
github.com/docker/go-metrics v0.0.1 // indirect
7071
github.com/docker/go-units v0.5.0 // indirect
7172
github.com/emirpasic/gods v1.18.1 // indirect
73+
github.com/fatih/color v1.16.0 // indirect
7274
github.com/felixge/httpsnoop v1.0.4 // indirect
7375
github.com/fvbommel/sortorder v1.0.1 // indirect
7476
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
@@ -96,6 +98,7 @@ require (
9698
github.com/kevinburke/ssh_config v1.2.0 // indirect
9799
github.com/klauspost/compress v1.18.0 // indirect
98100
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
101+
github.com/mattn/go-colorable v0.1.13 // indirect
99102
github.com/mattn/go-isatty v0.0.20 // indirect
100103
github.com/mattn/go-runewidth v0.0.15 // indirect
101104
github.com/mattn/go-shellwords v1.0.12 // indirect
@@ -164,6 +167,7 @@ require (
164167
golang.org/x/text v0.24.0 // indirect
165168
golang.org/x/time v0.11.0 // indirect
166169
golang.org/x/tools v0.32.0 // indirect
170+
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
167171
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 // indirect
168172
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
169173
google.golang.org/grpc v1.69.4 // indirect

go.sum

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o
140140
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
141141
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
142142
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
143+
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
144+
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
143145
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
144146
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
145147
github.com/fvbommel/sortorder v1.0.1 h1:dSnXLt4mJYH25uDDGa3biZNQsozaUWDSWeKJ0qqFfzE=
@@ -167,6 +169,8 @@ github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68=
167169
github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
168170
github.com/go-viper/mapstructure/v2 v2.0.0 h1:dhn8MZ1gZ0mzeodTG3jt5Vj/o87xZKuNAprG2mQfMfc=
169171
github.com/go-viper/mapstructure/v2 v2.0.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
172+
github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I=
173+
github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
170174
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
171175
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
172176
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
@@ -241,6 +245,9 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
241245
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
242246
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
243247
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
248+
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
249+
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
250+
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
244251
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
245252
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
246253
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
@@ -479,6 +486,7 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
479486
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
480487
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
481488
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
489+
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
482490
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
483491
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
484492
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
@@ -504,6 +512,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
504512
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
505513
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
506514
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
515+
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
516+
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
507517
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
508518
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38 h1:2oV8dfuIkM1Ti7DwXc0BJfnwr9csz4TDXI9EmiI+Rbw=
509519
google.golang.org/genproto/googleapis/api v0.0.0-20241021214115-324edc3d5d38/go.mod h1:vuAjtvlwkDKF6L1GQ0SokiRLCGFfeBUXWr/aFFkHACc=

internal/compose/definition.go

Lines changed: 124 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -6,116 +6,155 @@ import (
66
"github.com/docker/docker-language-server/internal/pkg/document"
77
"github.com/docker/docker-language-server/internal/tliron/glsp/protocol"
88
"github.com/docker/docker-language-server/internal/types"
9-
"gopkg.in/yaml.v3"
9+
"github.com/goccy/go-yaml/ast"
10+
"github.com/goccy/go-yaml/token"
1011
)
1112

12-
func Definition(ctx context.Context, definitionLinkSupport bool, doc document.ComposeDocument, params *protocol.DefinitionParams) (any, error) {
13-
line := int(params.Position.Line) + 1
14-
character := int(params.Position.Character) + 1
15-
root := doc.RootNode()
16-
if len(root.Content) > 0 {
17-
for i := 0; i < len(root.Content[0].Content); i += 2 {
18-
switch root.Content[0].Content[i].Value {
19-
case "services":
20-
for _, service := range root.Content[0].Content[i+1].Content {
21-
for j := 0; j < len(service.Content); j += 2 {
22-
if service.Content[j].Value == "depends_on" {
23-
if service.Content[j+1].Kind == yaml.MappingNode {
24-
for k := 0; k < len(service.Content[j+1].Content); k += 2 {
25-
link := dependencyLink(root, definitionLinkSupport, params, service.Content[j+1].Content[k], line, character, "services")
26-
if link != nil {
27-
return link, nil
28-
}
29-
}
30-
}
31-
if service.Content[j+1].Kind == yaml.SequenceNode {
32-
for _, dependency := range service.Content[j+1].Content {
33-
link := dependencyLink(root, definitionLinkSupport, params, dependency, line, character, "services")
34-
if link != nil {
35-
return link, nil
36-
}
37-
}
38-
}
39-
}
40-
41-
link := lookupDependencyLink(root, definitionLinkSupport, params, service, j, line, character, "configs")
42-
if link != nil {
43-
return link, nil
44-
}
13+
func findSequenceDependencyToken(attributeNode *ast.MappingValueNode, attributeName string, line, column int) (string, *token.Token) {
14+
if dependencies, ok := attributeNode.Value.(*ast.SequenceNode); ok {
15+
for _, dependency := range dependencies.Values {
16+
dependencyToken := dependency.GetToken()
17+
if dependencyToken.Position.Line == line && dependencyToken.Position.Column <= column && column <= dependencyToken.Position.Column+len(dependencyToken.Value) {
18+
return attributeName, dependencyToken
19+
}
20+
}
21+
}
22+
return "", nil
23+
}
4524

46-
link = lookupDependencyLink(root, definitionLinkSupport, params, service, j, line, character, "networks")
47-
if link != nil {
48-
return link, nil
49-
}
25+
func findDependencyToken(attributeNode *ast.MappingValueNode, attributeName string, line, column int) (string, *token.Token) {
26+
if attributeNode.Key.GetToken().Value == attributeName {
27+
return findSequenceDependencyToken(attributeNode, attributeName, line, column)
28+
}
29+
return "", nil
30+
}
5031

51-
link = lookupDependencyLink(root, definitionLinkSupport, params, service, j, line, character, "secrets")
52-
if link != nil {
53-
return link, nil
32+
func lookupReference(serviceNode *ast.MappingValueNode, line, column int) (string, *token.Token) {
33+
if serviceAttributes, ok := serviceNode.Value.(*ast.MappingNode); ok {
34+
for _, attributeNode := range serviceAttributes.Values {
35+
if attributeNode.Key.GetToken().Value == "depends_on" {
36+
if _, ok := attributeNode.Value.(*ast.SequenceNode); ok {
37+
reference, dependency := findSequenceDependencyToken(attributeNode, "services", line, column)
38+
if dependency != nil {
39+
return reference, dependency
40+
}
41+
} else if serviceAttributes, ok := attributeNode.Value.(*ast.MappingNode); ok {
42+
for _, dependency := range serviceAttributes.Values {
43+
dependencyToken := dependency.Key.GetToken()
44+
if dependencyToken.Position.Line == line && dependencyToken.Position.Column <= column && column <= dependencyToken.Position.Column+len(dependencyToken.Value) {
45+
return "services", dependencyToken
5446
}
5547
}
5648
}
5749
}
50+
51+
reference, dependency := findDependencyToken(attributeNode, "configs", line, column)
52+
if dependency != nil {
53+
return reference, dependency
54+
}
55+
reference, dependency = findDependencyToken(attributeNode, "networks", line, column)
56+
if dependency != nil {
57+
return reference, dependency
58+
}
59+
reference, dependency = findDependencyToken(attributeNode, "secrets", line, column)
60+
if dependency != nil {
61+
return reference, dependency
62+
}
5863
}
5964
}
60-
return nil, nil
65+
return "", nil
6166
}
6267

63-
func lookupDependencyLink(root yaml.Node, definitionLinkSupport bool, params *protocol.DefinitionParams, service *yaml.Node, index, line, character int, nodeName string) any {
64-
if service.Content[index].Value == nodeName && service.Content[index+1].Kind == yaml.SequenceNode {
65-
for _, dependency := range service.Content[index+1].Content {
66-
link := dependencyLink(root, definitionLinkSupport, params, dependency, line, character, nodeName)
67-
if link != nil {
68-
return link
68+
func lookupDependency(node *ast.MappingValueNode, line, column int) (string, *token.Token) {
69+
if s, ok := node.Key.(*ast.StringNode); ok && s.Value == "services" {
70+
if servicesNode, ok := node.Value.(*ast.MappingNode); ok {
71+
for _, serviceNode := range servicesNode.Values {
72+
reference, dependency := lookupReference(serviceNode, line, column)
73+
if dependency != nil {
74+
return reference, dependency
75+
}
6976
}
77+
} else if valueNode, ok := node.Value.(*ast.MappingValueNode); ok {
78+
return lookupReference(valueNode, line, column)
7079
}
7180
}
72-
return nil
81+
return "", nil
7382
}
7483

75-
func dependencyLink(root yaml.Node, definitionLinkSupport bool, params *protocol.DefinitionParams, dependency *yaml.Node, line, character int, nodeName string) any {
76-
if dependency.Line == line && dependency.Column <= character && character <= dependency.Column+len(dependency.Value) {
77-
serviceRange := serviceDefinitionRange(root, nodeName, dependency.Value)
78-
if serviceRange == nil {
79-
return nil
84+
func findDefinition(node *ast.MappingValueNode, referenceType, referenceName string) *token.Token {
85+
if s, ok := node.Key.(*ast.StringNode); ok && s.Value == referenceType {
86+
if servicesNode, ok := node.Value.(*ast.MappingNode); ok {
87+
for _, serviceNode := range servicesNode.Values {
88+
if serviceNode.Key.GetToken().Value == referenceName {
89+
return serviceNode.Key.GetToken()
90+
}
91+
}
92+
} else if networks, ok := node.Value.(*ast.MappingValueNode); ok {
93+
if networks.Key.GetToken().Value == referenceName {
94+
return networks.Key.GetToken()
95+
}
8096
}
81-
82-
return types.CreateDefinitionResult(
83-
definitionLinkSupport,
84-
*serviceRange,
85-
&protocol.Range{
86-
Start: protocol.Position{
87-
Line: params.Position.Line,
88-
Character: protocol.UInteger(dependency.Column - 1),
89-
},
90-
End: protocol.Position{
91-
Line: params.Position.Line,
92-
Character: protocol.UInteger(dependency.Column + len(dependency.Value) - 1),
93-
},
94-
},
95-
params.TextDocument.URI,
96-
)
9797
}
9898
return nil
9999
}
100100

101-
func serviceDefinitionRange(root yaml.Node, nodeName, serviceName string) *protocol.Range {
102-
for i := 0; i < len(root.Content[0].Content); i += 2 {
103-
if root.Content[0].Content[i].Value == nodeName {
104-
for _, service := range root.Content[0].Content[i+1].Content {
105-
if service.Value == serviceName {
106-
return &protocol.Range{
107-
Start: protocol.Position{
108-
Line: protocol.UInteger(service.Line) - 1,
109-
Character: protocol.UInteger(service.Column - 1),
110-
},
111-
End: protocol.Position{
112-
Line: protocol.UInteger(service.Line) - 1,
113-
Character: protocol.UInteger(service.Column + len(serviceName) - 1),
114-
},
101+
func Definition(ctx context.Context, definitionLinkSupport bool, doc document.ComposeDocument, params *protocol.DefinitionParams) (any, error) {
102+
line := int(params.Position.Line) + 1
103+
character := int(params.Position.Character) + 1
104+
105+
file := doc.File()
106+
if file == nil || len(file.Docs) == 0 {
107+
return nil, nil
108+
}
109+
110+
if mappingNode, ok := file.Docs[0].Body.(*ast.MappingNode); ok {
111+
for _, node := range mappingNode.Values {
112+
reference, dependency := lookupDependency(node, line, character)
113+
if dependency != nil {
114+
for _, node := range mappingNode.Values {
115+
referenced := findDefinition(node, reference, dependency.Value)
116+
if referenced != nil {
117+
return dependencyLink(definitionLinkSupport, params, referenced, dependency), nil
115118
}
116119
}
120+
return nil, nil
121+
}
122+
}
123+
} else if mappingNodeValue, ok := file.Docs[0].Body.(*ast.MappingValueNode); ok {
124+
reference, dependency := lookupDependency(mappingNodeValue, line, character)
125+
if dependency != nil {
126+
referenced := findDefinition(mappingNodeValue, reference, dependency.Value)
127+
if referenced != nil {
128+
return dependencyLink(definitionLinkSupport, params, referenced, dependency), nil
117129
}
118130
}
119131
}
120-
return nil
132+
return nil, nil
133+
}
134+
135+
func dependencyLink(definitionLinkSupport bool, params *protocol.DefinitionParams, referenced, dependency *token.Token) any {
136+
return types.CreateDefinitionResult(
137+
definitionLinkSupport,
138+
protocol.Range{
139+
Start: protocol.Position{
140+
Line: protocol.UInteger(referenced.Position.Line - 1),
141+
Character: protocol.UInteger(referenced.Position.Column - 1),
142+
},
143+
End: protocol.Position{
144+
Line: protocol.UInteger(referenced.Position.Line - 1),
145+
Character: protocol.UInteger(referenced.Position.Column + len(referenced.Value) - 1),
146+
},
147+
},
148+
&protocol.Range{
149+
Start: protocol.Position{
150+
Line: params.Position.Line,
151+
Character: protocol.UInteger(dependency.Position.Column - 1),
152+
},
153+
End: protocol.Position{
154+
Line: params.Position.Line,
155+
Character: protocol.UInteger(dependency.Position.Column + len(dependency.Value) - 1),
156+
},
157+
},
158+
params.TextDocument.URI,
159+
)
121160
}

internal/compose/definition_test.go

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -157,37 +157,41 @@ services:
157157
services:
158158
test:
159159
image: alpine:3.21
160+
depends_on:
161+
- redis
160162
configs:
161-
- def
163+
- def
164+
redis:
165+
image: redis
162166
163167
configs:
164168
def:
165169
file: ./httpd.conf`,
166-
line: 5,
167-
character: 8,
170+
line: 7,
171+
character: 10,
168172
locations: []protocol.Location{
169173
{
170174
URI: composeFileURI,
171175
Range: protocol.Range{
172-
Start: protocol.Position{Line: 8, Character: 2},
173-
End: protocol.Position{Line: 8, Character: 5},
176+
Start: protocol.Position{Line: 12, Character: 2},
177+
End: protocol.Position{Line: 12, Character: 5},
174178
},
175179
},
176180
},
177181
links: []protocol.LocationLink{
178182
{
179183
OriginSelectionRange: &protocol.Range{
180-
Start: protocol.Position{Line: 5, Character: 6},
181-
End: protocol.Position{Line: 5, Character: 9},
184+
Start: protocol.Position{Line: 7, Character: 8},
185+
End: protocol.Position{Line: 7, Character: 11},
182186
},
183187
TargetURI: composeFileURI,
184188
TargetRange: protocol.Range{
185-
Start: protocol.Position{Line: 8, Character: 2},
186-
End: protocol.Position{Line: 8, Character: 5},
189+
Start: protocol.Position{Line: 12, Character: 2},
190+
End: protocol.Position{Line: 12, Character: 5},
187191
},
188192
TargetSelectionRange: protocol.Range{
189-
Start: protocol.Position{Line: 8, Character: 2},
190-
End: protocol.Position{Line: 8, Character: 5},
193+
Start: protocol.Position{Line: 12, Character: 2},
194+
End: protocol.Position{Line: 12, Character: 5},
191195
},
192196
},
193197
},

0 commit comments

Comments
 (0)