From 6bf09d609191c0d0e50c9ef348f099e1a839ecdf Mon Sep 17 00:00:00 2001 From: Remy Suen Date: Fri, 6 Jun 2025 14:40:49 -0400 Subject: [PATCH] Resolve YAML anchors when hovering Signed-off-by: Remy Suen --- CHANGELOG.md | 8 +++ internal/compose/hover.go | 26 ++++++---- internal/compose/hover_test.go | 89 ++++++++++++++++++++++++++++++++++ 3 files changed, 113 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 464b0d2..a043fb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ All notable changes to the Docker Language Server will be documented in this file. +## [Unreleased] + +### Added + +- Compose + - textDocument/hover + - resolve anchors when constructing the path of the hovered item ([#303](https://github.com/docker/docker-language-server/issues/303)) + ## [0.10.2] - 2025-06-06 ### Fixed diff --git a/internal/compose/hover.go b/internal/compose/hover.go index e7d12f8..41f0b0e 100644 --- a/internal/compose/hover.go +++ b/internal/compose/hover.go @@ -379,18 +379,24 @@ func hover(schema *jsonschema.Schema, nodes []ast.Node, line, column, lineLength } func constructNodePath(matches []ast.Node, node ast.Node, line, col int) []ast.Node { + if anchor, ok := node.(*ast.AnchorNode); ok { + node = anchor.Value + } switch n := node.(type) { case *ast.MappingValueNode: - if keyNode, ok := n.Key.(*ast.StringNode); ok { - if m := constructNodePath(matches, n.Key, line, col); m != nil { - matches = append(matches, m...) - return matches - } - if m := constructNodePath(matches, n.Value, line, col); m != nil { - matches = append(matches, keyNode) - matches = append(matches, m...) - return matches - } + var nodeKey ast.Node + nodeKey = n.Key + if anchor, ok := nodeKey.(*ast.AnchorNode); ok { + nodeKey = anchor.Value + } + if m := constructNodePath(matches, nodeKey, line, col); m != nil { + matches = append(matches, m...) + return matches + } + if m := constructNodePath(matches, n.Value, line, col); m != nil { + matches = append(matches, nodeKey) + matches = append(matches, m...) + return matches } case *ast.MappingNode: for _, kv := range n.Values { diff --git a/internal/compose/hover_test.go b/internal/compose/hover_test.go index 02a3a18..ae32b94 100644 --- a/internal/compose/hover_test.go +++ b/internal/compose/hover_test.go @@ -180,6 +180,24 @@ services: }, }, }, + { + name: "recursive enum values when hovering over the attribute's value with an anchor", + content: ` +services: + test: + volumes: + - type: bind + bind: + recursive: &anchor enabled`, + line: 6, + character: 32, + result: &protocol.Hover{ + Contents: protocol.MarkupContent{ + Kind: protocol.MarkupKindMarkdown, + Value: "Recursively mount the source directory.\n\nAllowed values:\n- `disabled`\n- `enabled`\n- `readonly`\n- `writable`\n\nSchema: [compose-spec.json](https://raw.githubusercontent.com/compose-spec/compose-spec/master/schema/compose-spec.json)\n\n[Online documentation](https://docs.docker.com/reference/compose-file/services/#volumes)", + }, + }, + }, { name: "recursive enum values when hovering over the attribute's value at the end", content: ` @@ -282,6 +300,21 @@ services: }, }, }, + { + name: "mapping node defined by an anchor", + content: ` +services: + &anchor abc: + image: alpine:3.21`, + line: 3, + character: 7, + result: &protocol.Hover{ + Contents: protocol.MarkupContent{ + Kind: protocol.MarkupKindMarkdown, + Value: "Specify the image to start the container from. Can be a repository/tag, a digest, or a local image ID.\n\nSchema: [compose-spec.json](https://raw.githubusercontent.com/compose-spec/compose-spec/master/schema/compose-spec.json)\n\n[Online documentation](https://docs.docker.com/reference/compose-file/services/#image)", + }, + }, + }, { name: "hovering over an invalid extends object with invalid attribute", content: ` @@ -873,6 +906,31 @@ services: backend: volumes: - db-data +volumes: + db-data: + driver: custom`, + line: 4, + character: 12, + result: &protocol.Hover{ + Contents: protocol.MarkupContent{ + Kind: protocol.MarkupKindMarkdown, + Value: "```YAML\n" + `db-data: + driver: custom` + + "\n```", + }, + Range: &protocol.Range{ + Start: protocol.Position{Line: 4, Character: 8}, + End: protocol.Position{Line: 4, Character: 15}, + }, + }, + }, + { + name: "volumes hover as an array string with anchor definition", + content: ` +services: + backend: + volumes: &anchor + - db-data volumes: db-data: driver: custom`, @@ -996,6 +1054,37 @@ services: volume: nocopy: true subpath: sub +volumes: + db-data: + driver: custom`, + line: 6, + character: 20, + result: &protocol.Hover{ + Contents: protocol.MarkupContent{ + Kind: protocol.MarkupKindMarkdown, + Value: "```YAML\n" + `db-data: + driver: custom` + + "\n```", + }, + Range: &protocol.Range{ + Start: protocol.Position{Line: 6, Character: 16}, + End: protocol.Position{Line: 6, Character: 23}, + }, + }, + }, + { + name: "volumes hover as an array object with an object anchor at the top", + content: ` +services: + backend: &anchor + image: example/backend + volumes: + - type: volume + source: db-data + target: /data + volume: + nocopy: true + subpath: sub volumes: db-data: driver: custom`,