Skip to content

Commit 005c742

Browse files
committed
Allow Compose support to be disabled on initialization
We are introducing an experimental option to disable all Compose features on initiailization to prevent conflicts and duplicates with editors that provide their own native Compose editing features. Signed-off-by: Remy Suen <[email protected]>
1 parent e537a22 commit 005c742

18 files changed

+234
-38
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,14 @@ Use "docker-language-server [command] --help" for more information about a comma
8686
On startup, the client can include initialization options on the initial `initialize` request.
8787
1. If the client is also using [rcjsuen/dockerfile-language-server](https://github.com/rcjsuen/dockerfile-language-server), then some results in `textDocument/publishDiagnostics` will be duplicated across the two language servers. By setting the _experimental_ `dockerfileExperimental.removeOverlappingIssues` to `true`, the Docker Language Server will suppress the duplicated results. Note that this setting may be renamed or removed at any time.
8888
2. Telemetry can be configured on server startup with the `telemetry` field. You can read more about this in [TELEMETRY.md](./TELEMETRY.md).
89+
3. Compose support can be disabled on server initialization by setting the _experimental_ `dockercomposeExperimental.composeSupport` attribute to `false`. The default value is `true`.
8990

9091
```JSONC
9192
{
9293
"initializationOptions": {
94+
"dockercomposeExperimental": {
95+
"composeSupport:": true | false
96+
},
9397
"dockerfileExperimental": {
9498
"removeOverlappingIssues:": true | false
9599
},

e2e-tests/completion_test.go

Lines changed: 102 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package server_test
33
import (
44
"bytes"
55
"context"
6+
"fmt"
67
"os"
78
"testing"
89

@@ -12,7 +13,7 @@ import (
1213
"github.com/stretchr/testify/require"
1314
)
1415

15-
func TestCompletion(t *testing.T) {
16+
func TestCompletion_Bake(t *testing.T) {
1617
s := startServer()
1718

1819
client := bytes.NewBuffer(make([]byte, 0, 1024))
@@ -100,7 +101,7 @@ func TestCompletion(t *testing.T) {
100101

101102
for _, tc := range testCases {
102103
t.Run(tc.name, func(t *testing.T) {
103-
didOpen := createDidOpenTextDocumentParams(homedir, t.Name()+".hcl", tc.content, "dockerbake")
104+
didOpen := createDidOpenTextDocumentParams(homedir, t.Name()+".hcl", tc.content, protocol.DockerBakeLanguage)
104105
err := conn.Notify(context.Background(), protocol.MethodTextDocumentDidOpen, didOpen)
105106
require.NoError(t, err)
106107

@@ -117,3 +118,102 @@ func TestCompletion(t *testing.T) {
117118
})
118119
}
119120
}
121+
122+
func TestCompletion_Compose(t *testing.T) {
123+
testCompletion_Compose(t, true)
124+
testCompletion_Compose(t, false)
125+
}
126+
127+
func testCompletion_Compose(t *testing.T, composeSupport bool) {
128+
s := startServer()
129+
130+
client := bytes.NewBuffer(make([]byte, 0, 1024))
131+
server := bytes.NewBuffer(make([]byte, 0, 1024))
132+
serverStream := &TestStream{incoming: server, outgoing: client, closed: false}
133+
defer serverStream.Close()
134+
go s.ServeStream(serverStream)
135+
136+
clientStream := jsonrpc2.NewBufferedStream(&TestStream{incoming: client, outgoing: server, closed: false}, jsonrpc2.VSCodeObjectCodec{})
137+
defer clientStream.Close()
138+
conn := jsonrpc2.NewConn(context.Background(), clientStream, &ConfigurationHandler{t: t})
139+
initialize(t, conn, protocol.InitializeParams{
140+
InitializationOptions: map[string]any{
141+
"dockercomposeExperimental": map[string]bool{"composeSupport": composeSupport},
142+
},
143+
})
144+
145+
homedir, err := os.UserHomeDir()
146+
require.NoError(t, err)
147+
148+
testCases := []struct {
149+
name string
150+
content string
151+
line uint32
152+
character uint32
153+
items []protocol.CompletionItem
154+
}{
155+
{
156+
name: "empty file",
157+
content: "",
158+
line: 0,
159+
character: 0,
160+
items: []protocol.CompletionItem{
161+
{
162+
Label: "configs",
163+
Documentation: "Configurations that are shared among multiple services.",
164+
},
165+
{
166+
Label: "include",
167+
Documentation: "compose sub-projects to be included.",
168+
},
169+
{
170+
Label: "name",
171+
Documentation: "define the Compose project name, until user defines one explicitly.",
172+
},
173+
{
174+
Label: "networks",
175+
Documentation: "Networks that are shared among multiple services.",
176+
},
177+
{
178+
Label: "secrets",
179+
Documentation: "Secrets that are shared among multiple services.",
180+
},
181+
{
182+
Label: "services",
183+
Documentation: "The services that will be used by your application.",
184+
},
185+
{
186+
Label: "version",
187+
Documentation: "declared for backward compatibility, ignored. Please remove it.",
188+
},
189+
{
190+
Label: "volumes",
191+
Documentation: "Named volumes that are shared among multiple services.",
192+
},
193+
},
194+
},
195+
}
196+
197+
for _, tc := range testCases {
198+
t.Run(fmt.Sprintf("%v (composeSupport=%v)", tc.name, composeSupport), func(t *testing.T) {
199+
didOpen := createDidOpenTextDocumentParams(homedir, t.Name()+".yaml", tc.content, protocol.DockerComposeLanguage)
200+
err := conn.Notify(context.Background(), protocol.MethodTextDocumentDidOpen, didOpen)
201+
require.NoError(t, err)
202+
203+
var result *protocol.CompletionList
204+
err = conn.Call(context.Background(), protocol.MethodTextDocumentCompletion, protocol.CompletionParams{
205+
TextDocumentPositionParams: protocol.TextDocumentPositionParams{
206+
TextDocument: protocol.TextDocumentIdentifier{URI: didOpen.TextDocument.URI},
207+
Position: protocol.Position{Line: 0, Character: 0},
208+
},
209+
}, &result)
210+
require.NoError(t, err)
211+
if composeSupport {
212+
require.False(t, result.IsIncomplete)
213+
require.Equal(t, tc.items, result.Items)
214+
} else {
215+
require.Nil(t, result)
216+
}
217+
})
218+
}
219+
}

e2e-tests/hover_test.go

Lines changed: 64 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"context"
66
"encoding/json"
7+
"fmt"
78
"os"
89
"testing"
910

@@ -91,12 +92,63 @@ func TestHover(t *testing.T) {
9192
},
9293
},
9394
},
95+
}
96+
97+
for _, tc := range testCases {
98+
t.Run(tc.name, func(t *testing.T) {
99+
didOpen := createDidOpenTextDocumentParams(homedir, t.Name()+tc.fileExtensionSuffix, tc.content, tc.languageID)
100+
err := conn.Notify(context.Background(), protocol.MethodTextDocumentDidOpen, didOpen)
101+
require.NoError(t, err)
102+
103+
var hover *protocol.Hover
104+
err = conn.Call(context.Background(), protocol.MethodTextDocumentHover, protocol.HoverParams{
105+
TextDocumentPositionParams: protocol.TextDocumentPositionParams{
106+
TextDocument: protocol.TextDocumentIdentifier{URI: didOpen.TextDocument.URI},
107+
Position: protocol.Position{Line: 0, Character: 3},
108+
},
109+
}, &hover)
110+
require.NoError(t, err)
111+
require.Equal(t, tc.result, hover)
112+
})
113+
}
114+
}
115+
116+
func TestHover_Compose(t *testing.T) {
117+
testHover_Compose(t, true)
118+
testHover_Compose(t, false)
119+
}
120+
121+
func testHover_Compose(t *testing.T, composeSupport bool) {
122+
s := startServer()
123+
124+
client := bytes.NewBuffer(make([]byte, 0, 1024))
125+
server := bytes.NewBuffer(make([]byte, 0, 1024))
126+
serverStream := &TestStream{incoming: server, outgoing: client, closed: false}
127+
defer serverStream.Close()
128+
go s.ServeStream(serverStream)
129+
130+
clientStream := jsonrpc2.NewBufferedStream(&TestStream{incoming: client, outgoing: server, closed: false}, jsonrpc2.VSCodeObjectCodec{})
131+
defer clientStream.Close()
132+
conn := jsonrpc2.NewConn(context.Background(), clientStream, &ConfigurationHandler{t: t})
133+
initialize(t, conn, protocol.InitializeParams{
134+
InitializationOptions: map[string]any{
135+
"dockercomposeExperimental": map[string]bool{"composeSupport": composeSupport},
136+
},
137+
})
138+
139+
homedir, err := os.UserHomeDir()
140+
require.NoError(t, err)
141+
142+
testCases := []struct {
143+
name string
144+
content string
145+
position protocol.Position
146+
result *protocol.Hover
147+
}{
94148
{
95-
languageID: protocol.DockerComposeLanguage,
96-
fileExtensionSuffix: ".yaml",
97-
name: "version description",
98-
content: "version: 1.2.3",
99-
position: protocol.Position{Line: 0, Character: 4},
149+
name: "version description",
150+
content: "version: 1.2.3",
151+
position: protocol.Position{Line: 0, Character: 4},
100152
result: &protocol.Hover{
101153
Contents: protocol.MarkupContent{
102154
Kind: protocol.MarkupKindMarkdown,
@@ -107,8 +159,8 @@ func TestHover(t *testing.T) {
107159
}
108160

109161
for _, tc := range testCases {
110-
t.Run(tc.name, func(t *testing.T) {
111-
didOpen := createDidOpenTextDocumentParams(homedir, t.Name()+tc.fileExtensionSuffix, tc.content, tc.languageID)
162+
t.Run(fmt.Sprintf("%v (composeSupport=%v)", tc.name, composeSupport), func(t *testing.T) {
163+
didOpen := createDidOpenTextDocumentParams(homedir, t.Name()+".yaml", tc.content, protocol.DockerComposeLanguage)
112164
err := conn.Notify(context.Background(), protocol.MethodTextDocumentDidOpen, didOpen)
113165
require.NoError(t, err)
114166

@@ -120,8 +172,11 @@ func TestHover(t *testing.T) {
120172
},
121173
}, &hover)
122174
require.NoError(t, err)
123-
require.Equal(t, tc.result, hover)
175+
if composeSupport {
176+
require.Equal(t, tc.result, hover)
177+
} else {
178+
require.Nil(t, hover)
179+
}
124180
})
125181
}
126-
127182
}

e2e-tests/prepareRename_test.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package server_test
33
import (
44
"bytes"
55
"context"
6+
"fmt"
67
"os"
78
"testing"
89

@@ -12,6 +13,11 @@ import (
1213
)
1314

1415
func TestPrepareRename(t *testing.T) {
16+
testPrepareRename(t, true)
17+
testPrepareRename(t, false)
18+
}
19+
20+
func testPrepareRename(t *testing.T, composeSupport bool) {
1521
s := startServer()
1622

1723
client := bytes.NewBuffer(make([]byte, 0, 1024))
@@ -23,7 +29,11 @@ func TestPrepareRename(t *testing.T) {
2329
clientStream := jsonrpc2.NewBufferedStream(&TestStream{incoming: client, outgoing: server, closed: false}, jsonrpc2.VSCodeObjectCodec{})
2430
defer clientStream.Close()
2531
conn := jsonrpc2.NewConn(context.Background(), clientStream, &ConfigurationHandler{t: t})
26-
initialize(t, conn, protocol.InitializeParams{})
32+
initialize(t, conn, protocol.InitializeParams{
33+
InitializationOptions: map[string]any{
34+
"dockercomposeExperimental": map[string]bool{"composeSupport": composeSupport},
35+
},
36+
})
2737

2838
homedir, err := os.UserHomeDir()
2939
require.NoError(t, err)
@@ -51,8 +61,8 @@ services:
5161
}
5262

5363
for _, tc := range testCases {
54-
t.Run(tc.name, func(t *testing.T) {
55-
didOpen := createDidOpenTextDocumentParams(homedir, t.Name()+".yaml", tc.content, "dockercompose")
64+
t.Run(fmt.Sprintf("%v (composeSupport=%v)", tc.name, composeSupport), func(t *testing.T) {
65+
didOpen := createDidOpenTextDocumentParams(homedir, t.Name()+".yaml", tc.content, protocol.DockerComposeLanguage)
5666
err := conn.Notify(context.Background(), protocol.MethodTextDocumentDidOpen, didOpen)
5767
require.NoError(t, err)
5868

@@ -64,7 +74,11 @@ services:
6474
},
6575
}, &result)
6676
require.NoError(t, err)
67-
require.Equal(t, tc.result, result)
77+
if composeSupport {
78+
require.Equal(t, tc.result, result)
79+
} else {
80+
require.Nil(t, result)
81+
}
6882
})
6983
}
7084
}

e2e-tests/rename_test.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package server_test
33
import (
44
"bytes"
55
"context"
6+
"fmt"
67
"os"
78
"testing"
89

@@ -12,6 +13,11 @@ import (
1213
)
1314

1415
func TestRename(t *testing.T) {
16+
testRename(t, true)
17+
testRename(t, false)
18+
}
19+
20+
func testRename(t *testing.T, composeSupport bool) {
1521
s := startServer()
1622

1723
client := bytes.NewBuffer(make([]byte, 0, 1024))
@@ -23,7 +29,11 @@ func TestRename(t *testing.T) {
2329
clientStream := jsonrpc2.NewBufferedStream(&TestStream{incoming: client, outgoing: server, closed: false}, jsonrpc2.VSCodeObjectCodec{})
2430
defer clientStream.Close()
2531
conn := jsonrpc2.NewConn(context.Background(), clientStream, &ConfigurationHandler{t: t})
26-
initialize(t, conn, protocol.InitializeParams{})
32+
initialize(t, conn, protocol.InitializeParams{
33+
InitializationOptions: map[string]any{
34+
"dockercomposeExperimental": map[string]bool{"composeSupport": composeSupport},
35+
},
36+
})
2737

2838
homedir, err := os.UserHomeDir()
2939
require.NoError(t, err)
@@ -69,8 +79,8 @@ services:
6979
}
7080

7181
for _, tc := range testCases {
72-
t.Run(tc.name, func(t *testing.T) {
73-
didOpen := createDidOpenTextDocumentParams(homedir, t.Name()+".yaml", tc.content, "dockercompose")
82+
t.Run(fmt.Sprintf("%v (composeSupport=%v)", tc.name, composeSupport), func(t *testing.T) {
83+
didOpen := createDidOpenTextDocumentParams(homedir, t.Name()+".yaml", tc.content, protocol.DockerComposeLanguage)
7484
err := conn.Notify(context.Background(), protocol.MethodTextDocumentDidOpen, didOpen)
7585
require.NoError(t, err)
7686

@@ -83,7 +93,11 @@ services:
8393
NewName: "newName",
8494
}, &workspaceEdit)
8595
require.NoError(t, err)
86-
require.Equal(t, tc.workspaceEdit(didOpen.TextDocument.URI), workspaceEdit)
96+
if composeSupport {
97+
require.Equal(t, tc.workspaceEdit(didOpen.TextDocument.URI), workspaceEdit)
98+
} else {
99+
require.Nil(t, workspaceEdit)
100+
}
87101
})
88102
}
89103
}

internal/pkg/server/completion.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func (s *Server) TextDocumentCompletion(ctx *glsp.Context, params *protocol.Comp
1818

1919
if doc.LanguageIdentifier() == protocol.DockerBakeLanguage {
2020
return hcl.Completion(ctx.Context, params, s.docs, doc.(document.BakeHCLDocument))
21-
} else if doc.LanguageIdentifier() == protocol.DockerComposeLanguage && s.composeCompletion {
21+
} else if doc.LanguageIdentifier() == protocol.DockerComposeLanguage && s.composeSupport && s.composeCompletion {
2222
return compose.Completion(ctx.Context, params, s.docs, doc.(document.ComposeDocument))
2323
}
2424
return nil, nil

internal/pkg/server/definition.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func (s *Server) TextDocumentDefinition(ctx *glsp.Context, params *protocol.Defi
1818

1919
if doc.LanguageIdentifier() == protocol.DockerBakeLanguage {
2020
return hcl.Definition(ctx.Context, s.definitionLinkSupport, s.docs, uri.URI(params.TextDocument.URI), doc.(document.BakeHCLDocument), params.Position)
21-
} else if doc.LanguageIdentifier() == protocol.DockerComposeLanguage {
21+
} else if doc.LanguageIdentifier() == protocol.DockerComposeLanguage && s.composeSupport {
2222
return compose.Definition(ctx.Context, s.definitionLinkSupport, doc.(document.ComposeDocument), params)
2323
}
2424
return nil, nil

internal/pkg/server/documentHighlight.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ func (s *Server) TextDocumentDocumentHighlight(ctx *glsp.Context, params *protoc
1717
defer doc.Close()
1818
if doc.LanguageIdentifier() == protocol.DockerBakeLanguage {
1919
return hcl.DocumentHighlight(doc.(document.BakeHCLDocument), params.Position)
20-
} else if doc.LanguageIdentifier() == protocol.DockerComposeLanguage {
20+
} else if doc.LanguageIdentifier() == protocol.DockerComposeLanguage && s.composeSupport {
2121
return compose.DocumentHighlight(doc.(document.ComposeDocument), params.Position)
2222
}
2323
return nil, nil

internal/pkg/server/documentLink.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ func (s *Server) TextDocumentDocumentLink(ctx *glsp.Context, params *protocol.Do
1818
language := doc.LanguageIdentifier()
1919
if language == protocol.DockerBakeLanguage {
2020
return hcl.DocumentLink(ctx.Context, params.TextDocument.URI, doc.(document.BakeHCLDocument))
21-
} else if language == protocol.DockerComposeLanguage {
21+
} else if language == protocol.DockerComposeLanguage && s.composeSupport {
2222
return compose.DocumentLink(ctx.Context, params.TextDocument.URI, doc.(document.ComposeDocument))
2323
}
2424
return nil, nil

0 commit comments

Comments
 (0)