Skip to content

Commit d1506c0

Browse files
authored
Merge pull request #168 from docker/report-compose-syntax-errors
Report YAML syntax errors in Compose files
2 parents e8f8481 + df73945 commit d1506c0

File tree

6 files changed

+117
-3
lines changed

6 files changed

+117
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ All notable changes to the Docker Language Server will be documented in this fil
1414
- show the parent service's value if it is being overridden and they are not object attributes ([#156](https://github.com/docker/docker-language-server/issues/156))
1515
- textDocument/formatting
1616
- add support to format YAML files that do not have clear syntactical errors ([#165](https://github.com/docker/docker-language-server/issues/165))
17+
- textDocument/publishDiagnostics
18+
- report YAML syntax errors ([#167](https://github.com/docker/docker-language-server/issues/167))
1719

1820
### Fixed
1921

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ The Docker Language Server relies on some features that are dependent on [Buildx
1616
- code completion
1717
- code navigation
1818
- document outline support
19+
- error reporting
1920
- formatting
2021
- highlight named references of services, networks, volumes, configs, and secrets
2122
- hover tooltips
@@ -27,6 +28,7 @@ The Docker Language Server relies on some features that are dependent on [Buildx
2728
- code completion
2829
- code navigation
2930
- document outline support
31+
- error reporting
3032
- formatting
3133
- hover tooltips
3234
- inferring variable values
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package compose
2+
3+
import (
4+
"errors"
5+
"math"
6+
7+
"github.com/docker/docker-language-server/internal/pkg/document"
8+
"github.com/docker/docker-language-server/internal/pkg/lsp/textdocument"
9+
"github.com/docker/docker-language-server/internal/tliron/glsp/protocol"
10+
"github.com/docker/docker-language-server/internal/types"
11+
"github.com/goccy/go-yaml"
12+
)
13+
14+
type ComposeDiagnosticsCollector struct {
15+
}
16+
17+
func NewComposeDiagnosticsCollector() textdocument.DiagnosticsCollector {
18+
return &ComposeDiagnosticsCollector{}
19+
}
20+
21+
func (c *ComposeDiagnosticsCollector) SupportsLanguageIdentifier(languageIdentifier protocol.LanguageIdentifier) bool {
22+
return languageIdentifier == protocol.DockerComposeLanguage
23+
}
24+
25+
func (c *ComposeDiagnosticsCollector) CollectDiagnostics(source, workspaceFolder string, doc document.Document, text string) []protocol.Diagnostic {
26+
err := doc.(document.ComposeDocument).ParsingError()
27+
if err != nil {
28+
var syntaxError *yaml.SyntaxError
29+
if errors.As(err, &syntaxError) {
30+
return []protocol.Diagnostic{
31+
{
32+
Message: syntaxError.Message,
33+
Source: types.CreateStringPointer(source),
34+
Severity: types.CreateDiagnosticSeverityPointer(protocol.DiagnosticSeverityError),
35+
Range: protocol.Range{
36+
Start: protocol.Position{
37+
Line: protocol.UInteger(syntaxError.Token.Position.Line) - 1,
38+
Character: protocol.UInteger(syntaxError.Token.Position.Column) - 1,
39+
},
40+
End: protocol.Position{
41+
Line: protocol.UInteger(syntaxError.Token.Position.Line) - 1,
42+
Character: math.MaxUint32,
43+
},
44+
},
45+
},
46+
}
47+
}
48+
}
49+
return nil
50+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package compose
2+
3+
import (
4+
"fmt"
5+
"math"
6+
"os"
7+
"path/filepath"
8+
"strings"
9+
"testing"
10+
11+
"github.com/docker/docker-language-server/internal/pkg/document"
12+
"github.com/docker/docker-language-server/internal/tliron/glsp/protocol"
13+
"github.com/docker/docker-language-server/internal/types"
14+
"github.com/stretchr/testify/require"
15+
"go.lsp.dev/uri"
16+
)
17+
18+
func TestCollectDiagnostics(t *testing.T) {
19+
testCases := []struct {
20+
name string
21+
content string
22+
diagnostics []protocol.Diagnostic
23+
}{
24+
{
25+
name: "tab is flagged as an error",
26+
content: `
27+
service:
28+
abc:`,
29+
diagnostics: []protocol.Diagnostic{
30+
{
31+
Message: "found character '\t' that cannot start any token",
32+
Source: types.CreateStringPointer("docker-language-server"),
33+
Severity: types.CreateDiagnosticSeverityPointer(protocol.DiagnosticSeverityError),
34+
Range: protocol.Range{
35+
Start: protocol.Position{Line: 2, Character: 0},
36+
End: protocol.Position{Line: 2, Character: math.MaxUint32},
37+
},
38+
},
39+
},
40+
},
41+
}
42+
43+
composeFileURI := uri.URI(fmt.Sprintf("file:///%v", strings.TrimPrefix(filepath.ToSlash(filepath.Join(os.TempDir(), "compose.yaml")), "/")))
44+
for _, tc := range testCases {
45+
t.Run(tc.name, func(t *testing.T) {
46+
collector := NewComposeDiagnosticsCollector()
47+
doc := document.NewComposeDocument(composeFileURI, 1, []byte(tc.content))
48+
diagnostics := collector.CollectDiagnostics("docker-language-server", "", doc, "")
49+
require.Equal(t, tc.diagnostics, diagnostics)
50+
})
51+
}
52+
}

internal/pkg/document/dockerComposeDocument.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ import (
1212
type ComposeDocument interface {
1313
Document
1414
File() *ast.File
15+
ParsingError() error
1516
}
1617

1718
type composeDocument struct {
1819
document
19-
mutex sync.Mutex
20-
file *ast.File
20+
mutex sync.Mutex
21+
file *ast.File
22+
parsingError error
2123
}
2224

2325
func NewComposeDocument(u uri.URI, version int32, input []byte) ComposeDocument {
@@ -39,7 +41,7 @@ func (d *composeDocument) parse(_ bool) bool {
3941
d.mutex.Lock()
4042
defer d.mutex.Unlock()
4143

42-
d.file, _ = parser.ParseBytes(d.input, 0)
44+
d.file, d.parsingError = parser.ParseBytes(d.input, 0)
4345
return true
4446
}
4547

@@ -50,3 +52,7 @@ func (d *composeDocument) copy() Document {
5052
func (d *composeDocument) File() *ast.File {
5153
return d.file
5254
}
55+
56+
func (d *composeDocument) ParsingError() error {
57+
return d.parsingError
58+
}

internal/pkg/server/server.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"time"
99

1010
"github.com/docker/docker-language-server/internal/bake/hcl"
11+
"github.com/docker/docker-language-server/internal/compose"
1112
"github.com/docker/docker-language-server/internal/configuration"
1213
"github.com/docker/docker-language-server/internal/pkg/buildkit"
1314
"github.com/docker/docker-language-server/internal/pkg/cli/metadata"
@@ -90,6 +91,7 @@ func NewServer(docManager *document.Manager) *Server {
9091
diagnosticsCollectors: []textdocument.DiagnosticsCollector{
9192
buildkit.NewBuildKitDiagnosticsCollector(),
9293
scoutService,
94+
compose.NewComposeDiagnosticsCollector(),
9395
hcl.NewBakeHCLDiagnosticsCollector(docManager, scoutService),
9496
},
9597
}

0 commit comments

Comments
 (0)