diff --git a/internal/lsp/diagnostics.go b/internal/lsp/diagnostics.go index db8eeb59cd..f4ff11d437 100644 --- a/internal/lsp/diagnostics.go +++ b/internal/lsp/diagnostics.go @@ -44,6 +44,14 @@ func (s *Server) diagnose(ctx context.Context, snapshot source.Snapshot, alwaysA ctx, done := trace.StartSpan(ctx, "lsp:background-worker") defer done() + // Wait for a free diagnostics slot. + select { + case <-ctx.Done(): + return nil + case s.diagnosticsSema <- struct{}{}: + } + defer func() { <-s.diagnosticsSema }() + allReports := make(map[diagnosticKey][]source.Diagnostic) var reportsMu sync.Mutex var wg sync.WaitGroup diff --git a/internal/lsp/lsp_test.go b/internal/lsp/lsp_test.go index a893834575..d6febccd16 100644 --- a/internal/lsp/lsp_test.go +++ b/internal/lsp/lsp_test.go @@ -85,12 +85,9 @@ func testLSP(t *testing.T, exporter packagestest.Exporter) { t.Fatal(err) } r := &runner{ - server: &Server{ - session: session, - delivered: map[span.URI]sentDiagnostics{}, - }, - data: datum, - ctx: ctx, + server: NewServer(session, nil), + data: datum, + ctx: ctx, } t.Run(datum.Folder, func(t *testing.T) { t.Helper() diff --git a/internal/lsp/server.go b/internal/lsp/server.go index ed1c00c935..1aad63af5b 100644 --- a/internal/lsp/server.go +++ b/internal/lsp/server.go @@ -16,13 +16,16 @@ import ( "golang.org/x/tools/internal/span" ) +const concurrentAnalyses = 1 + // NewServer creates an LSP server and binds it to handle incoming client // messages on on the supplied stream. func NewServer(session source.Session, client protocol.Client) *Server { return &Server{ - delivered: make(map[span.URI]sentDiagnostics), - session: session, - client: client, + delivered: make(map[span.URI]sentDiagnostics), + session: session, + client: client, + diagnosticsSema: make(chan struct{}, concurrentAnalyses), } } @@ -54,6 +57,9 @@ type Server struct { // delivered is a cache of the diagnostics that the server has sent. deliveredMu sync.Mutex delivered map[span.URI]sentDiagnostics + + // diagnosticsSema limits the concurrency of diagnostics runs, which can be expensive. + diagnosticsSema chan struct{} } // sentDiagnostics is used to cache diagnostics that have been sent for a given file.