Skip to content

Commit 97f1ac2

Browse files
authored
Merge pull request #32 from docker/always-return-location-links
Prefer to return LocationLinks if the client supports it
2 parents 532b1ff + 69f6b68 commit 97f1ac2

File tree

3 files changed

+670
-141
lines changed

3 files changed

+670
-141
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ All notable changes to the Docker Language Server will be documented in this fil
1010
- textDocument/publishDiagnostics
1111
- introduce a setting to ignore certain diagnostics to not duplicate the ones from the Dockerfile Language Server
1212

13+
### Fixed
14+
15+
- Docker Bake
16+
- textDocument/definition
17+
- always return LocationLinks to help disambiguate word boundaries for clients ([#31](https://github.com/docker/docker-language-server/issues/31))
18+
1319
## 0.1.0 - 2025-03-31
1420

1521
### Added

internal/bake/hcl/definition.go

Lines changed: 142 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -89,21 +89,30 @@ func Definition(ctx context.Context, definitionLinkSupport bool, manager *docume
8989

9090
for _, attribute := range body.Attributes {
9191
if isInsideRange(attribute.NameRange, position) {
92-
return []protocol.Location{
93-
{
94-
Range: protocol.Range{
95-
Start: protocol.Position{
96-
Line: uint32(attribute.NameRange.Start.Line) - 1,
97-
Character: uint32(attribute.NameRange.Start.Column) - 1,
98-
},
99-
End: protocol.Position{
100-
Line: uint32(attribute.NameRange.End.Line) - 1,
101-
Character: uint32(attribute.NameRange.End.Column) - 1,
102-
},
92+
return createDefinitionResult(
93+
definitionLinkSupport,
94+
protocol.Range{
95+
Start: protocol.Position{
96+
Line: uint32(attribute.NameRange.Start.Line) - 1,
97+
Character: uint32(attribute.NameRange.Start.Column) - 1,
98+
},
99+
End: protocol.Position{
100+
Line: uint32(attribute.NameRange.End.Line) - 1,
101+
Character: uint32(attribute.NameRange.End.Column) - 1,
102+
},
103+
},
104+
&protocol.Range{
105+
Start: protocol.Position{
106+
Line: uint32(attribute.NameRange.Start.Line) - 1,
107+
Character: uint32(attribute.NameRange.Start.Column) - 1,
108+
},
109+
End: protocol.Position{
110+
Line: uint32(attribute.NameRange.End.Line) - 1,
111+
Character: uint32(attribute.NameRange.End.Column) - 1,
103112
},
104-
URI: string(documentURI),
105113
},
106-
}, nil
114+
string(documentURI),
115+
), nil
107116
}
108117

109118
if isInsideRange(attribute.SrcRange, position) {
@@ -126,7 +135,18 @@ func ResolveAttributeValue(ctx context.Context, definitionLinkSupport bool, mana
126135
(sourceBlock.Type == "group" && attribute.Name == "targets") {
127136
value, _ := templateExpr.Value(&hcl.EvalContext{})
128137
target := value.AsString()
129-
return CalculateBlockLocation(input, body, documentURI, "target", target, false)
138+
templateExprRange := templateExpr.Range()
139+
sourceRange := hcl.Range{
140+
Start: hcl.Pos{
141+
Line: templateExprRange.Start.Line,
142+
Column: templateExprRange.Start.Column + 1,
143+
},
144+
End: hcl.Pos{
145+
Line: templateExprRange.End.Line,
146+
Column: templateExprRange.End.Column - 1,
147+
},
148+
}
149+
return CalculateBlockLocation(definitionLinkSupport, input, body, documentURI, sourceRange, "target", target, false)
130150
}
131151
}
132152
}
@@ -158,32 +178,30 @@ func ResolveExpression(ctx context.Context, definitionLinkSupport bool, manager
158178
for _, child := range nodes {
159179
if strings.EqualFold(child.Value, "FROM") {
160180
if child.Next != nil && child.Next.Next != nil && strings.EqualFold(child.Next.Next.Value, "AS") && child.Next.Next.Next != nil && child.Next.Next.Next.Value == target {
161-
targetRange := protocol.Range{
162-
Start: protocol.Position{Line: uint32(child.StartLine) - 1, Character: 0},
163-
End: protocol.Position{Line: uint32(child.EndLine) - 1, Character: uint32(len(lines[child.EndLine-1]))},
164-
}
165-
166-
linkURI := protocol.URI(fmt.Sprintf("file:///%v", strings.TrimPrefix(filepath.ToSlash(dockerfilePath), "/")))
167-
if !definitionLinkSupport {
168-
return []protocol.Location{
169-
{
170-
Range: targetRange,
171-
URI: linkURI,
181+
return createDefinitionResult(
182+
definitionLinkSupport,
183+
protocol.Range{
184+
Start: protocol.Position{
185+
Line: uint32(child.StartLine) - 1,
186+
Character: 0,
172187
},
173-
}
174-
}
175-
176-
return []protocol.LocationLink{
177-
{
178-
OriginSelectionRange: &protocol.Range{
179-
Start: protocol.Position{Line: uint32(literalValueExpr.Range().Start.Line) - 1, Character: uint32(literalValueExpr.Range().Start.Column) - 1},
180-
End: protocol.Position{Line: uint32(literalValueExpr.Range().End.Line) - 1, Character: uint32(uint32(literalValueExpr.Range().End.Column) - 1)},
188+
End: protocol.Position{
189+
Line: uint32(child.EndLine) - 1,
190+
Character: uint32(len(lines[child.EndLine-1])),
181191
},
182-
TargetRange: targetRange,
183-
TargetSelectionRange: targetRange,
184-
TargetURI: linkURI,
185192
},
186-
}
193+
&protocol.Range{
194+
Start: protocol.Position{
195+
Line: uint32(literalValueExpr.Range().Start.Line) - 1,
196+
Character: uint32(literalValueExpr.Range().Start.Column) - 1,
197+
},
198+
End: protocol.Position{
199+
Line: uint32(literalValueExpr.Range().End.Line) - 1,
200+
Character: uint32(uint32(literalValueExpr.Range().End.Column) - 1),
201+
},
202+
},
203+
protocol.URI(fmt.Sprintf("file:///%v", strings.TrimPrefix(filepath.ToSlash(dockerfilePath), "/"))),
204+
)
187205
}
188206
}
189207
}
@@ -225,15 +243,29 @@ func ResolveExpression(ctx context.Context, definitionLinkSupport bool, manager
225243
}
226244

227245
if value == arg {
228-
return []protocol.Location{
229-
{
230-
Range: protocol.Range{
231-
Start: protocol.Position{Line: uint32(node.StartLine) - 1, Character: 0},
232-
End: protocol.Position{Line: uint32(node.EndLine) - 1, Character: uint32(len(lines[node.EndLine-1]))},
233-
},
234-
URI: protocol.URI(fmt.Sprintf("file:///%v", strings.TrimPrefix(filepath.ToSlash(dockerfilePath), "/"))),
246+
originSelectionRange := protocol.Range{
247+
Start: protocol.Position{
248+
Line: uint32(item.KeyExpr.Range().Start.Line) - 1,
249+
Character: uint32(item.KeyExpr.Range().Start.Column) - 1,
235250
},
251+
End: protocol.Position{
252+
Line: uint32(item.KeyExpr.Range().End.Line) - 1,
253+
Character: uint32(item.KeyExpr.Range().End.Column) - 1,
254+
},
255+
}
256+
if LiteralValue(item.KeyExpr) {
257+
originSelectionRange.Start.Character = originSelectionRange.Start.Character + 1
258+
originSelectionRange.End.Character = originSelectionRange.End.Character - 1
236259
}
260+
return createDefinitionResult(
261+
definitionLinkSupport,
262+
protocol.Range{
263+
Start: protocol.Position{Line: uint32(node.StartLine) - 1, Character: 0},
264+
End: protocol.Position{Line: uint32(node.EndLine) - 1, Character: uint32(len(lines[node.EndLine-1]))},
265+
},
266+
&originSelectionRange,
267+
protocol.URI(fmt.Sprintf("file:///%v", strings.TrimPrefix(filepath.ToSlash(dockerfilePath), "/"))),
268+
)
237269
}
238270
child = child.Next
239271
}
@@ -278,7 +310,7 @@ func ResolveExpression(ctx context.Context, definitionLinkSupport bool, manager
278310

279311
if _, ok := expression.(*hclsyntax.ScopeTraversalExpr); ok {
280312
name := string(input[expression.Range().Start.Byte:expression.Range().End.Byte])
281-
return CalculateBlockLocation(input, body, documentURI, "variable", name, true)
313+
return CalculateBlockLocation(definitionLinkSupport, input, body, documentURI, expression.Range(), "variable", name, true)
282314
}
283315

284316
if templateWrapExpr, ok := expression.(*hclsyntax.TemplateWrapExpr); ok {
@@ -287,7 +319,7 @@ func ResolveExpression(ctx context.Context, definitionLinkSupport bool, manager
287319

288320
if functionCallExpr, ok := expression.(*hclsyntax.FunctionCallExpr); ok {
289321
if isInsideRange(functionCallExpr.NameRange, position) {
290-
return CalculateBlockLocation(input, body, documentURI, "function", functionCallExpr.Name, true)
322+
return CalculateBlockLocation(definitionLinkSupport, input, body, documentURI, functionCallExpr.NameRange, "function", functionCallExpr.Name, true)
291323
}
292324

293325
for _, arg := range functionCallExpr.Args {
@@ -303,7 +335,7 @@ func ResolveExpression(ctx context.Context, definitionLinkSupport bool, manager
303335
// returns it. If variable is true then it will also look at the
304336
// top-level attributes of the HCL file and resolve to those if the
305337
// names match.
306-
func CalculateBlockLocation(input []byte, body *hclsyntax.Body, documentURI uri.URI, blockName, name string, variable bool) any {
338+
func CalculateBlockLocation(definitionLinkSupport bool, input []byte, body *hclsyntax.Body, documentURI uri.URI, sourceRange hcl.Range, blockName, name string, variable bool) any {
307339
for _, b := range body.Blocks {
308340
if b.Type == blockName && b.Labels[0] == name {
309341
startCharacter := uint32(b.LabelRanges[0].Start.Column)
@@ -315,42 +347,80 @@ func CalculateBlockLocation(input []byte, body *hclsyntax.Body, documentURI uri.
315347
startCharacter--
316348
endCharacter--
317349
}
318-
return []protocol.Location{
319-
{
320-
Range: protocol.Range{
321-
Start: protocol.Position{
322-
Line: uint32(b.LabelRanges[0].Start.Line) - 1,
323-
Character: startCharacter,
324-
},
325-
End: protocol.Position{
326-
Line: uint32(b.LabelRanges[0].End.Line) - 1,
327-
Character: endCharacter,
328-
},
350+
return createDefinitionResult(
351+
definitionLinkSupport,
352+
protocol.Range{
353+
Start: protocol.Position{
354+
Line: uint32(b.LabelRanges[0].Start.Line) - 1,
355+
Character: startCharacter,
356+
},
357+
End: protocol.Position{
358+
Line: uint32(b.LabelRanges[0].End.Line) - 1,
359+
Character: endCharacter,
329360
},
330-
URI: string(documentURI),
331361
},
332-
}
362+
&protocol.Range{
363+
Start: protocol.Position{
364+
Line: uint32(sourceRange.Start.Line) - 1,
365+
Character: uint32(sourceRange.Start.Column) - 1,
366+
},
367+
End: protocol.Position{
368+
Line: uint32(sourceRange.End.Line) - 1,
369+
Character: uint32(sourceRange.End.Column) - 1,
370+
},
371+
},
372+
string(documentURI),
373+
)
333374
}
334375
}
335376

336377
if attribute, ok := body.Attributes[name]; ok && variable {
378+
return createDefinitionResult(
379+
definitionLinkSupport,
380+
protocol.Range{
381+
Start: protocol.Position{
382+
Line: uint32(attribute.NameRange.Start.Line) - 1,
383+
Character: uint32(attribute.NameRange.Start.Column) - 1,
384+
},
385+
End: protocol.Position{
386+
Line: uint32(attribute.NameRange.End.Line) - 1,
387+
Character: uint32(attribute.NameRange.End.Column) - 1,
388+
},
389+
},
390+
&protocol.Range{
391+
Start: protocol.Position{
392+
Line: uint32(sourceRange.Start.Line) - 1,
393+
Character: uint32(sourceRange.Start.Column) - 1,
394+
},
395+
End: protocol.Position{
396+
Line: uint32(sourceRange.End.Line) - 1,
397+
Character: uint32(sourceRange.End.Column) - 1,
398+
},
399+
},
400+
string(documentURI),
401+
)
402+
}
403+
return nil
404+
}
405+
406+
func createDefinitionResult(definitionLinkSupport bool, targetRange protocol.Range, originSelectionRange *protocol.Range, linkURI protocol.URI) any {
407+
if !definitionLinkSupport {
337408
return []protocol.Location{
338409
{
339-
Range: protocol.Range{
340-
Start: protocol.Position{
341-
Line: uint32(attribute.NameRange.Start.Line) - 1,
342-
Character: uint32(attribute.NameRange.Start.Column) - 1,
343-
},
344-
End: protocol.Position{
345-
Line: uint32(attribute.NameRange.End.Line) - 1,
346-
Character: uint32(attribute.NameRange.End.Column) - 1,
347-
},
348-
},
349-
URI: string(documentURI),
410+
Range: targetRange,
411+
URI: linkURI,
350412
},
351413
}
352414
}
353-
return nil
415+
416+
return []protocol.LocationLink{
417+
{
418+
OriginSelectionRange: originSelectionRange,
419+
TargetRange: targetRange,
420+
TargetSelectionRange: targetRange,
421+
TargetURI: linkURI,
422+
},
423+
}
354424
}
355425

356426
func ParseDockerfile(dockerfilePath string) ([]byte, *parser.Result, error) {

0 commit comments

Comments
 (0)