Skip to content

Commit a017089

Browse files
authored
Merge pull request #136 from docker/dependent-service-compose-completions
Suggest dependent services for the depends_on attribute
2 parents 2e347a0 + 4cca99b commit a017089

File tree

3 files changed

+144
-3
lines changed

3 files changed

+144
-3
lines changed

CHANGELOG.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ All notable changes to the Docker Language Server will be documented in this fil
66

77
### Added
88

9-
- updated Compose schema to the latest version ([#117](https://github.com/docker/docker-language-server/issues/117))
9+
- Compose
10+
- updated Compose schema to the latest version ([#117](https://github.com/docker/docker-language-server/issues/117))
11+
- textDocument/completion
12+
- suggest dependent service names for the `depends_on` attribute ([#131](https://github.com/docker/docker-language-server/issues/131))
1013

1114
### Fixed
1215

internal/compose/completion.go

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616

1717
func prefix(line string, character int) string {
1818
sb := strings.Builder{}
19-
for i := 0; i < character; i++ {
19+
for i := range character {
2020
if unicode.IsSpace(rune(line[i])) {
2121
sb.Reset()
2222
} else {
@@ -62,9 +62,14 @@ func Completion(ctx context.Context, params *protocol.CompletionParams, doc docu
6262
return nil, nil
6363
}
6464

65+
wordPrefix := prefix(lines[lspLine], character-1)
66+
dependencies := dependencyCompletionItems(file, path, params, protocol.UInteger(len(wordPrefix)))
67+
if len(dependencies) > 0 {
68+
return &protocol.CompletionList{Items: dependencies}, nil
69+
}
70+
6571
items := []protocol.CompletionItem{}
6672
nodeProps := nodeProperties(path, line, character)
67-
wordPrefix := prefix(lines[lspLine], character-1)
6873
if schema, ok := nodeProps.(*jsonschema.Schema); ok {
6974
if schema.Enum != nil {
7075
for _, value := range schema.Enum.Values {
@@ -151,6 +156,52 @@ func Completion(ctx context.Context, params *protocol.CompletionParams, doc docu
151156
return &protocol.CompletionList{Items: items}, nil
152157
}
153158

159+
func findServices(file *ast.File) []string {
160+
services := []string{}
161+
for _, documentNode := range file.Docs {
162+
if mappingNode, ok := documentNode.Body.(*ast.MappingNode); ok {
163+
for _, n := range mappingNode.Values {
164+
if s, ok := n.Key.(*ast.StringNode); ok {
165+
if s.Value == "services" {
166+
if mappingNode, ok := n.Value.(*ast.MappingNode); ok {
167+
for _, service := range mappingNode.Values {
168+
services = append(services, service.Key.GetToken().Value)
169+
}
170+
}
171+
}
172+
}
173+
}
174+
}
175+
}
176+
return services
177+
}
178+
179+
func dependencyCompletionItems(file *ast.File, path []*ast.MappingValueNode, params *protocol.CompletionParams, prefixLength protocol.UInteger) []protocol.CompletionItem {
180+
if len(path) == 3 && path[2].Key.GetToken().Value == "depends_on" {
181+
items := []protocol.CompletionItem{}
182+
for _, service := range findServices(file) {
183+
if service != path[1].Key.GetToken().Value {
184+
item := protocol.CompletionItem{
185+
Label: service,
186+
TextEdit: protocol.TextEdit{
187+
NewText: service,
188+
Range: protocol.Range{
189+
Start: protocol.Position{
190+
Line: params.Position.Line,
191+
Character: params.Position.Character - prefixLength,
192+
},
193+
End: params.Position,
194+
},
195+
},
196+
}
197+
items = append(items, item)
198+
}
199+
}
200+
return items
201+
}
202+
return nil
203+
}
204+
154205
func constructCompletionNodePath(file *ast.File, line int) []*ast.MappingValueNode {
155206
for _, documentNode := range file.Docs {
156207
if mappingNode, ok := documentNode.Body.(*ast.MappingNode); ok {

internal/compose/completion_test.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1832,6 +1832,93 @@ networks:
18321832
character: 2,
18331833
list: nil,
18341834
},
1835+
{
1836+
name: "depends_on array items",
1837+
content: `
1838+
services:
1839+
test:
1840+
image: alpine
1841+
depends_on:
1842+
-
1843+
test2:
1844+
image: alpine`,
1845+
line: 5,
1846+
character: 8,
1847+
list: &protocol.CompletionList{
1848+
Items: []protocol.CompletionItem{
1849+
{
1850+
Label: "test2",
1851+
TextEdit: textEdit("test2", 5, 8, 0),
1852+
},
1853+
},
1854+
},
1855+
},
1856+
{
1857+
name: "depends_on array items across two files",
1858+
content: `
1859+
---
1860+
services:
1861+
test:
1862+
image: alpine
1863+
depends_on:
1864+
-
1865+
---
1866+
services:
1867+
test2:
1868+
image: alpine`,
1869+
line: 6,
1870+
character: 8,
1871+
list: &protocol.CompletionList{
1872+
Items: []protocol.CompletionItem{
1873+
{
1874+
Label: "test2",
1875+
TextEdit: textEdit("test2", 6, 8, 0),
1876+
},
1877+
},
1878+
},
1879+
},
1880+
{
1881+
name: "depends_on array items",
1882+
content: `
1883+
services:
1884+
test:
1885+
image: alpine
1886+
depends_on:
1887+
- t
1888+
test2:
1889+
image: alpine`,
1890+
line: 5,
1891+
character: 9,
1892+
list: &protocol.CompletionList{
1893+
Items: []protocol.CompletionItem{
1894+
{
1895+
Label: "test2",
1896+
TextEdit: textEdit("test2", 5, 9, 1),
1897+
},
1898+
},
1899+
},
1900+
},
1901+
{
1902+
name: "depends_on service object",
1903+
content: `
1904+
services:
1905+
test:
1906+
image: alpine
1907+
depends_on:
1908+
1909+
test2:
1910+
image: alpine`,
1911+
line: 5,
1912+
character: 6,
1913+
list: &protocol.CompletionList{
1914+
Items: []protocol.CompletionItem{
1915+
{
1916+
Label: "test2",
1917+
TextEdit: textEdit("test2", 5, 6, 0),
1918+
},
1919+
},
1920+
},
1921+
},
18351922
}
18361923

18371924
composeFileURI := fmt.Sprintf("file:///%v", strings.TrimPrefix(filepath.ToSlash(filepath.Join(os.TempDir(), "compose.yaml")), "/"))

0 commit comments

Comments
 (0)