Skip to content

Commit 20c3535

Browse files
committed
Implement hcl parse diagnostics
1 parent a6b5551 commit 20c3535

File tree

5 files changed

+127
-0
lines changed

5 files changed

+127
-0
lines changed

internal/context/context.go

+15
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"github.com/hashicorp/terraform-ls/internal/filesystem"
88
"github.com/hashicorp/terraform-ls/internal/terraform/rootmodule"
99
"github.com/hashicorp/terraform-ls/internal/watcher"
10+
"github.com/hashicorp/terraform-ls/langserver/diagnostics"
1011
"github.com/sourcegraph/go-lsp"
1112
)
1213

@@ -33,6 +34,7 @@ var (
3334
ctxRootModuleWalker = &contextKey{"root module walker"}
3435
ctxRootModuleLoader = &contextKey{"root module loader"}
3536
ctxRootDir = &contextKey{"root directory"}
37+
ctxDiags = &contextKey{"diagnostics"}
3638
)
3739

3840
func missingContextErr(ctxKey *contextKey) *MissingContextErr {
@@ -211,3 +213,16 @@ func RootModuleLoader(ctx context.Context) (rootmodule.RootModuleLoader, error)
211213
}
212214
return w, nil
213215
}
216+
217+
func WithDiagnostics(ctx context.Context, diags *diagnostics.Notifier) context.Context {
218+
return context.WithValue(ctx, ctxDiags, diags)
219+
}
220+
221+
func Diagnostics(ctx context.Context) (*diagnostics.Notifier, error) {
222+
diags, ok := ctx.Value(ctxDiags).(*diagnostics.Notifier)
223+
if !ok {
224+
return nil, missingContextErr(ctxDiags)
225+
}
226+
227+
return diags, nil
228+
}

langserver/diagnostics/diagnostics.go

+91
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package diagnostics
2+
3+
import (
4+
"context"
5+
"sync"
6+
7+
"github.com/creachadair/jrpc2"
8+
"github.com/hashicorp/hcl/v2"
9+
"github.com/hashicorp/hcl/v2/hclparse"
10+
ilsp "github.com/hashicorp/terraform-ls/internal/lsp"
11+
"github.com/sourcegraph/go-lsp"
12+
)
13+
14+
type documentContext struct {
15+
ctx context.Context
16+
uri lsp.DocumentURI
17+
text []byte
18+
}
19+
20+
type Notifier struct {
21+
sessCtx context.Context
22+
hclDocs chan<- documentContext
23+
closeHclDocsOnce sync.Once
24+
}
25+
26+
func NewNotifier(sessCtx context.Context) *Notifier {
27+
hclDocs := make(chan documentContext, 10)
28+
go hclDiags(hclDocs)
29+
return &Notifier{hclDocs: hclDocs, sessCtx: sessCtx}
30+
}
31+
32+
func (n *Notifier) DiagnoseHCL(ctx context.Context, uri lsp.DocumentURI, text []byte) {
33+
select {
34+
case <-n.sessCtx.Done():
35+
n.closeHclDocsOnce.Do(func() {
36+
close(n.hclDocs)
37+
})
38+
return
39+
case n.hclDocs <- documentContext{ctx: ctx, uri: uri, text: text}:
40+
}
41+
}
42+
43+
func hclDiags(docs <-chan documentContext) {
44+
for d := range docs {
45+
diags := []lsp.Diagnostic{}
46+
47+
_, hclDiags := hclparse.NewParser().ParseHCL(d.text, string(d.uri))
48+
for _, hclDiag := range hclDiags {
49+
if hclDiag.Subject != nil {
50+
diags = append(diags, lsp.Diagnostic{
51+
Range: ilsp.HCLRangeToLSP(*hclDiag.Subject),
52+
Severity: hclSeverityToLSP(hclDiag.Severity),
53+
Source: "HCL",
54+
Message: lspMessage(hclDiag),
55+
})
56+
}
57+
}
58+
59+
if err := jrpc2.PushNotify(d.ctx, "textDocument/publishDiagnostics", lsp.PublishDiagnosticsParams{
60+
URI: d.uri,
61+
Diagnostics: diags,
62+
}); !acceptableError(err) {
63+
panic(err)
64+
}
65+
}
66+
}
67+
68+
func hclSeverityToLSP(severity hcl.DiagnosticSeverity) lsp.DiagnosticSeverity {
69+
var sev lsp.DiagnosticSeverity
70+
switch severity {
71+
case hcl.DiagError:
72+
sev = lsp.Error
73+
case hcl.DiagWarning:
74+
sev = lsp.Warning
75+
case hcl.DiagInvalid:
76+
panic("invalid diagnostic")
77+
}
78+
return sev
79+
}
80+
81+
func lspMessage(diag *hcl.Diagnostic) string {
82+
m := diag.Summary
83+
if diag.Detail != "" {
84+
m += ": " + diag.Detail
85+
}
86+
return m
87+
}
88+
89+
func acceptableError(err error) bool {
90+
return true
91+
}

langserver/handlers/did_change.go

+10
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,16 @@ func TextDocumentDidChange(ctx context.Context, params DidChangeTextDocumentPara
4949
return err
5050
}
5151

52+
diags, err := lsctx.Diagnostics(ctx)
53+
if err != nil {
54+
return err
55+
}
56+
text, err := f.Text()
57+
if err != nil {
58+
return err
59+
}
60+
diags.DiagnoseHCL(ctx, params.TextDocument.URI, text)
61+
5262
cf, err := lsctx.RootModuleCandidateFinder(ctx)
5363
if err != nil {
5464
return err

langserver/handlers/did_open.go

+7
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ import (
1414
)
1515

1616
func (lh *logHandler) TextDocumentDidOpen(ctx context.Context, params lsp.DidOpenTextDocumentParams) error {
17+
18+
diags, err := lsctx.Diagnostics(ctx)
19+
if err != nil {
20+
return err
21+
}
22+
diags.DiagnoseHCL(ctx, params.TextDocument.URI, []byte(params.TextDocument.Text))
23+
1724
fs, err := lsctx.DocumentStorage(ctx)
1825
if err != nil {
1926
return err

langserver/handlers/service.go

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/hashicorp/terraform-ls/internal/filesystem"
1616
"github.com/hashicorp/terraform-ls/internal/terraform/rootmodule"
1717
"github.com/hashicorp/terraform-ls/internal/watcher"
18+
"github.com/hashicorp/terraform-ls/langserver/diagnostics"
1819
"github.com/hashicorp/terraform-ls/langserver/session"
1920
"github.com/sourcegraph/go-lsp"
2021
)
@@ -141,6 +142,7 @@ func (svc *service) Assigner() (jrpc2.Assigner, error) {
141142
}
142143

143144
rmLoader := rootmodule.NewRootModuleLoader(svc.sessCtx, svc.modMgr)
145+
diags := diagnostics.NewNotifier(svc.sessCtx)
144146

145147
rootDir := ""
146148

@@ -173,6 +175,7 @@ func (svc *service) Assigner() (jrpc2.Assigner, error) {
173175
if err != nil {
174176
return nil, err
175177
}
178+
ctx = lsctx.WithDiagnostics(ctx, diags)
176179
ctx = lsctx.WithDocumentStorage(ctx, fs)
177180
ctx = lsctx.WithRootModuleCandidateFinder(ctx, svc.modMgr)
178181
return handle(ctx, req, TextDocumentDidChange)
@@ -182,6 +185,7 @@ func (svc *service) Assigner() (jrpc2.Assigner, error) {
182185
if err != nil {
183186
return nil, err
184187
}
188+
ctx = lsctx.WithDiagnostics(ctx, diags)
185189
ctx = lsctx.WithDocumentStorage(ctx, fs)
186190
ctx = lsctx.WithRootDirectory(ctx, &rootDir)
187191
ctx = lsctx.WithRootModuleCandidateFinder(ctx, svc.modMgr)

0 commit comments

Comments
 (0)