Repository: docker/docker-language-server (language: go)

Issue:
Suggest items when using code completion for an array's item
```YAML
services:
  test:
    image: alpine:3.21
    configs:
    - 
```
We should suggest the five properties of `service_config_or_secret` when invoking code completion on the fifth line.
https://github.com/docker/docker-language-server/blob/dccfb86565c7a2471fb06f924e030516cb83e7c1/internal/compose/compose-spec.json#L991-L997


Discussion / hints:

Fix patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a0ba8f..0701ce4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@ All notable changes to the Docker Language Server will be documented in this fil
   - textDocument/completion
     - add code completion support based on the JSON schema, extracting out attribute names and enum values ([#86](https://github.com/docker/docker-language-server/issues/86))
     - completion items are populated with a detail that corresponds to the possible types of the item ([#93](https://github.com/docker/docker-language-server/issues/93))
+    - suggests completion items for the attributes of an object inside an array ([#95](https://github.com/docker/docker-language-server/issues/95))
   - textDocument/definition
     - support lookup of `configs`, `networks`, and `secrets` referenced inside `services` object ([#91](https://github.com/docker/docker-language-server/issues/91))
 
diff --git a/internal/compose/completion.go b/internal/compose/completion.go
index 8cff157..aa49711 100644
--- a/internal/compose/completion.go
+++ b/internal/compose/completion.go
@@ -45,7 +45,7 @@ func Completion(ctx context.Context, params *protocol.CompletionParams, doc docu
 
 	line := int(lspLine) + 1
 	character := int(params.Position.Character) + 1
-	topLevel, content, _ := nodeStructure(line, root.Content[0].Content)
+	topLevel, _, _ := nodeStructure(line, root.Content[0].Content)
 	if len(topLevel) == 0 {
 		return nil, nil
 	} else if len(topLevel) == 1 {
@@ -60,12 +60,6 @@ func Completion(ctx context.Context, params *protocol.CompletionParams, doc docu
 		return nil, nil
 	}
 
-	for _, childNode := range content.Content {
-		if childNode.Line == line {
-			return nil, nil
-		}
-	}
-
 	items := []protocol.CompletionItem{}
 	for attributeName, schema := range nodeProperties(topLevel, line, character) {
 		items = append(items, protocol.CompletionItem{
diff --git a/internal/compose/schema.go b/internal/compose/schema.go
index 2e6c279..ec215f8 100644
--- a/internal/compose/schema.go
+++ b/internal/compose/schema.go
@@ -72,6 +72,15 @@ func recurseNodeProperties(nodes []*yaml.Node, line, column, nodeOffset int, pro
 					}
 				}
 			}
+			if schema, ok := prop.Ref.Items.(*jsonschema.Schema); ok {
+				for _, nested := range schema.OneOf {
+					if nested.Types != nil && slices.Contains(nested.Types.ToStrings(), "object") {
+						if len(nested.Properties) > 0 {
+							return recurseNodeProperties(nodes, line, column, nodeOffset+1, nested.Properties)
+						}
+					}
+				}
+			}
 		}
 
 		for _, schema := range prop.OneOf {
@@ -122,5 +131,5 @@ func recurseNodeProperties(nodes []*yaml.Node, line, column, nodeOffset int, pro
 		}
 		return prop.Properties
 	}
-	return nil
+	return properties
 }


Test patch:
diff --git a/internal/compose/completion_test.go b/internal/compose/completion_test.go
index 7bebbaf..291cc2d 100644
--- a/internal/compose/completion_test.go
+++ b/internal/compose/completion_test.go
@@ -401,6 +401,25 @@ services:
 				},
 			},
 		},
+		{
+			name: "inner attributes of the configs array object under service",
+			content: `
+services:
+  test:
+    configs:
+    - `,
+			line:      4,
+			character: 6,
+			list: &protocol.CompletionList{
+				Items: []protocol.CompletionItem{
+					{Label: "gid", Detail: types.CreateStringPointer("string")},
+					{Label: "mode", Detail: types.CreateStringPointer("number or string")},
+					{Label: "source", Detail: types.CreateStringPointer("string")},
+					{Label: "target", Detail: types.CreateStringPointer("string")},
+					{Label: "uid", Detail: types.CreateStringPointer("string")},
+				},
+			},
+		},
 		{
 			name: "inner attributes of the ulimits object under service",
 			content: `