From e13398c87d445997aaeb16b4bb4f847f4d048409 Mon Sep 17 00:00:00 2001 From: pjw Date: Thu, 21 Jan 2021 08:39:47 -0500 Subject: [PATCH] internal/lsp: display current diagnostics in the debug server The change displays outstanding diagnostics on the Client page of the debug server. Very occasionally gopls displays incorrect diagnostics or diagnositics that won't go away. It is possible that providing more complete information will help us find the causes, at least when the debug server is running. Change-Id: I01f2bbbfeac86e7296f57f4a9aad8e1e658babfa Reviewed-on: https://go-review.googlesource.com/c/tools/+/285252 Run-TryBot: Peter Weinberger gopls-CI: kokoro TryBot-Result: Go Bot Trust: Peter Weinberger Reviewed-by: Robert Findley --- internal/lsp/debug/serve.go | 34 +++++++++++++++++++++++++ internal/lsp/diagnostics.go | 48 +++++++++++++++++++++++++++++++++++ internal/lsp/lsprpc/lsprpc.go | 1 + 3 files changed, 83 insertions(+) diff --git a/internal/lsp/debug/serve.go b/internal/lsp/debug/serve.go index aec976ad6f..473518e433 100644 --- a/internal/lsp/debug/serve.go +++ b/internal/lsp/debug/serve.go @@ -181,6 +181,7 @@ type Client struct { Logfile string GoplsPath string ServerID string + Service protocol.Server } // A Server is an outgoing connection to a remote LSP server. @@ -314,6 +315,16 @@ func (i *Instance) getInfo(r *http.Request) interface{} { return template.HTML(buf.String()) } +func (i *Instance) AddService(s protocol.Server, session *cache.Session) { + for _, c := range i.State.clients { + if c.Session == session { + c.Service = s + return + } + } + stdlog.Printf("unable to find a Client to add the protocol.Server to") +} + func getMemory(r *http.Request) interface{} { var m runtime.MemStats runtime.ReadMemStats(&m) @@ -785,6 +796,29 @@ Using session: {{template "sessionlink" .Session.ID}}
{{if .DebugAddress}}Debug this client at: {{localAddress .DebugAddress}}
{{end}} Logfile: {{.Logfile}}
Gopls Path: {{.GoplsPath}}
+

Diagnostics

+{{/*Service: []protocol.Server; each server has map[uri]fileReports; + each fileReport: map[diagnosticSoure]diagnosticReport + diagnosticSource is one of 5 source + diagnosticReport: snapshotID and map[hash]*source.Diagnostic + sourceDiagnostic: struct { + Range protocol.Range + Message string + Source string + Code string + CodeHref string + Severity protocol.DiagnosticSeverity + Tags []protocol.DiagnosticTag + + Related []RelatedInformation + } + RelatedInformation: struct { + URI span.URI + Range protocol.Range + Message string + } + */}} +
    {{range $k, $v := .Service.Diagnostics}}
  • {{$k}}:
      {{range $v}}
    1. {{.}}
    2. {{end}}
  • {{end}}
{{end}} `)) diff --git a/internal/lsp/diagnostics.go b/internal/lsp/diagnostics.go index 4941f007ea..adf19ad86d 100644 --- a/internal/lsp/diagnostics.go +++ b/internal/lsp/diagnostics.go @@ -50,6 +50,23 @@ type fileReports struct { reports map[diagnosticSource]diagnosticReport } +func (d diagnosticSource) String() string { + switch d { + case modSource: + return "FromSource" + case gcDetailsSource: + return "FromGCDetails" + case analysisSource: + return "FromAnalysis" + case typeCheckSource: + return "FromTypeChecking" + case orphanedSource: + return "FromOrphans" + default: + return fmt.Sprintf("From?%d?", d) + } +} + // hashDiagnostics computes a hash to identify diags. func hashDiagnostics(diags ...*source.Diagnostic) string { source.SortDiagnostics(diags) @@ -546,3 +563,34 @@ func (s *Server) shouldIgnoreError(ctx context.Context, snapshot source.Snapshot }) return !hasGo } + +// Diagnostics formattedfor the debug server +// (all the relevant fields of Server are private) +// (The alternative is to export them) +func (s *Server) Diagnostics() map[string][]string { + ans := make(map[string][]string) + s.diagnosticsMu.Lock() + defer s.diagnosticsMu.Unlock() + for k, v := range s.diagnostics { + fn := k.Filename() + for typ, d := range v.reports { + if len(d.diags) == 0 { + continue + } + for _, dx := range d.diags { + ans[fn] = append(ans[fn], auxStr(dx, d, typ)) + } + } + } + return ans +} + +func auxStr(v *source.Diagnostic, d diagnosticReport, typ diagnosticSource) string { + // Tags? RelatedInformation? + msg := fmt.Sprintf("(%s)%q(source:%q,code:%q,severity:%s,snapshot:%d,type:%s)", + v.Range, v.Message, v.Source, v.Code, v.Severity, d.snapshotID, typ) + for _, r := range v.Related { + msg += fmt.Sprintf(" [%s:%s,%q]", r.URI.Filename(), r.Range, r.Message) + } + return msg +} diff --git a/internal/lsp/lsprpc/lsprpc.go b/internal/lsp/lsprpc/lsprpc.go index 623533f22a..51267910cf 100644 --- a/internal/lsp/lsprpc/lsprpc.go +++ b/internal/lsp/lsprpc/lsprpc.go @@ -61,6 +61,7 @@ func (s *StreamServer) ServeStream(ctx context.Context, conn jsonrpc2.Conn) erro server := s.serverForTest if server == nil { server = lsp.NewServer(session, client) + debug.GetInstance(ctx).AddService(server, session) } // Clients may or may not send a shutdown message. Make sure the server is // shut down.