internal/lsp: handle undelivered diagnostics

This change adds a cache of undelivered diagnostics on the server-side.
If we fail to send a diagnostic once, we will retry the next time that
the server sends diagnostics.

Change-Id: I161dfad8ea1d2cfdcee933baed2d6872dc03b0c0
Reviewed-on: https://go-review.googlesource.com/c/tools/+/167737
Run-TryBot: Rebecca Stambler <rstambler@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Cottrell <iancottrell@google.com>
This commit is contained in:
Rebecca Stambler 2019-03-14 17:19:01 -04:00
Родитель e2f00d1e07
Коммит cf22ef0385
3 изменённых файлов: 39 добавлений и 13 удалений

Просмотреть файл

@ -14,7 +14,7 @@ import (
)
func (s *Server) cacheAndDiagnose(ctx context.Context, uri span.URI, content string) error {
if err := s.setContent(ctx, uri, []byte(content)); err != nil {
if err := s.view.SetContent(ctx, uri, []byte(content)); err != nil {
return err
}
go func() {
@ -26,22 +26,40 @@ func (s *Server) cacheAndDiagnose(ctx context.Context, uri span.URI, content str
if err != nil {
return // handle error?
}
s.undeliveredMu.Lock()
defer s.undeliveredMu.Unlock()
for uri, diagnostics := range reports {
protocolDiagnostics, err := toProtocolDiagnostics(ctx, s.view, diagnostics)
if err != nil {
continue // handle errors?
if err := s.publishDiagnostics(ctx, uri, diagnostics); err != nil {
s.undelivered[uri] = diagnostics
continue
}
s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{
Diagnostics: protocolDiagnostics,
URI: protocol.NewURI(uri),
})
// In case we had old, undelivered diagnostics.
delete(s.undelivered, uri)
}
// Anytime we compute diagnostics, make sure to also send along any
// undelivered ones (only for remaining URIs).
for uri, diagnostics := range s.undelivered {
s.publishDiagnostics(ctx, uri, diagnostics)
// If we fail to deliver the same diagnostics twice, just give up.
delete(s.undelivered, uri)
}
}()
return nil
}
func (s *Server) setContent(ctx context.Context, uri span.URI, content []byte) error {
return s.view.SetContent(ctx, uri, content)
func (s *Server) publishDiagnostics(ctx context.Context, uri span.URI, diagnostics []source.Diagnostic) error {
protocolDiagnostics, err := toProtocolDiagnostics(ctx, s.view, diagnostics)
if err != nil {
return err
}
s.client.PublishDiagnostics(ctx, &protocol.PublishDiagnosticsParams{
Diagnostics: protocolDiagnostics,
URI: protocol.NewURI(uri),
})
return nil
}
func toProtocolDiagnostics(ctx context.Context, v source.View, diagnostics []source.Diagnostic) ([]protocol.Diagnostic, error) {

Просмотреть файл

@ -80,7 +80,13 @@ type Server struct {
textDocumentSyncKind protocol.TextDocumentSyncKind
view *cache.View
viewMu sync.Mutex
view *cache.View
// undelivered is a cache of any diagnostics that the server
// failed to deliver for some reason.
undeliveredMu sync.Mutex
undelivered map[span.URI][]source.Diagnostic
}
func (s *Server) Run(ctx context.Context) error {
@ -302,7 +308,9 @@ func (s *Server) DidSave(context.Context, *protocol.DidSaveTextDocumentParams) e
}
func (s *Server) DidClose(ctx context.Context, params *protocol.DidCloseTextDocumentParams) error {
s.setContent(ctx, span.NewURI(params.TextDocument.URI), nil)
if err := s.view.SetContent(ctx, span.NewURI(params.TextDocument.URI), nil); err != nil {
return err
}
return nil
}

Просмотреть файл

@ -21,9 +21,9 @@ import (
// package does not directly access the file system.
type View interface {
Logger() xlog.Logger
FileSet() *token.FileSet
GetFile(ctx context.Context, uri span.URI) (File, error)
SetContent(ctx context.Context, uri span.URI, content []byte) error
FileSet() *token.FileSet
}
// File represents a Go source file that has been type-checked. It is the input