internal/langserver/diagnostics/diagnostics.go (83 lines of code) (raw):

package diagnostics import ( "context" "log" "path/filepath" "sync" ilsp "github.com/Azure/azapi-lsp/internal/lsp" lsp "github.com/Azure/azapi-lsp/internal/protocol" "github.com/Azure/azapi-lsp/internal/uri" "github.com/hashicorp/hcl/v2" ) type diagContext struct { ctx context.Context uri lsp.DocumentURI diags []lsp.Diagnostic } type DiagnosticSource string type ClientNotifier interface { Notify(ctx context.Context, method string, params interface{}) error } // Notifier is a type responsible for queueing HCL diagnostics to be converted // and sent to the client type Notifier struct { logger *log.Logger diags chan diagContext clientNotifier ClientNotifier closeDiagsOnce sync.Once } func NewNotifier(clientNotifier ClientNotifier, logger *log.Logger) *Notifier { n := &Notifier{ logger: logger, diags: make(chan diagContext, 50), clientNotifier: clientNotifier, } go n.notify() return n } // PublishHCLDiags accepts a map of HCL diagnostics per file and queues them for publishing. // A dir path is passed which is joined with the filename keys of the map, to form a file URI. func (n *Notifier) PublishHCLDiags(ctx context.Context, dirPath string, diags Diagnostics) { select { case <-ctx.Done(): n.closeDiagsOnce.Do(func() { close(n.diags) }) return default: } for filename, ds := range diags { fileDiags := make([]lsp.Diagnostic, 0) for source, diags := range ds { fileDiags = append(fileDiags, ilsp.HCLDiagsToLSP(diags, string(source))...) } n.diags <- diagContext{ ctx: ctx, uri: lsp.DocumentURI(uri.FromPath(filepath.Join(dirPath, filename))), diags: fileDiags, } } } func (n *Notifier) notify() { for d := range n.diags { if err := n.clientNotifier.Notify(d.ctx, "textDocument/publishDiagnostics", lsp.PublishDiagnosticsParams{ URI: d.uri, Diagnostics: d.diags, }); err != nil { n.logger.Printf("Error pushing diagnostics: %s", err) } } } type Diagnostics map[string]map[DiagnosticSource]hcl.Diagnostics func NewDiagnostics() Diagnostics { return make(Diagnostics) } // EmptyRootDiagnostic allows emptying any diagnostics for // the whole directory which were published previously. func (d Diagnostics) EmptyRootDiagnostic() Diagnostics { d[""] = make(map[DiagnosticSource]hcl.Diagnostics) return d } func (d Diagnostics) Append(src string, diagsMap map[string]hcl.Diagnostics) Diagnostics { for uri, uriDiags := range diagsMap { if _, ok := d[uri]; !ok { d[uri] = make(map[DiagnosticSource]hcl.Diagnostics) } d[uri][DiagnosticSource(src)] = uriDiags } return d }