internal static async ValueTask <ImmutableArray <IDiagnosticSource> > GetWorkspacePullDocumentsAsync(RequestContext context, IGlobalOptionService globalOptions, CancellationToken cancellationToken) { Contract.ThrowIfNull(context.Solution); // If we're being called from razor, we do not support WorkspaceDiagnostics at all. For razor, workspace // diagnostics will be handled by razor itself, which will operate by calling into Roslyn and asking for // document-diagnostics instead. if (context.ServerKind == WellKnownLspServerKinds.RazorLspServer) { return(ImmutableArray <IDiagnosticSource> .Empty); } using var _ = ArrayBuilder <IDiagnosticSource> .GetInstance(out var result); var solution = context.Solution; var documentTrackingService = solution.Services.GetRequiredService <IDocumentTrackingService>(); // Collect all the documents from the solution in the order we'd like to get diagnostics for. This will // prioritize the files from currently active projects, but then also include all other docs in all projects // (depending on current FSA settings). var activeDocument = documentTrackingService.GetActiveDocument(solution); var visibleDocuments = documentTrackingService.GetVisibleDocuments(solution); // Now, prioritize the projects related to the active/visible files. await AddDocumentsAndProject(activeDocument?.Project, context.SupportedLanguages, isOpen : true, cancellationToken).ConfigureAwait(false); foreach (var doc in visibleDocuments) { await AddDocumentsAndProject(doc.Project, context.SupportedLanguages, isOpen : true, cancellationToken).ConfigureAwait(false); } // finally, add the remainder of all documents. foreach (var project in solution.Projects) { await AddDocumentsAndProject(project, context.SupportedLanguages, isOpen : false, cancellationToken).ConfigureAwait(false); } // Ensure that we only process documents once. result.RemoveDuplicates(); return(result.ToImmutable()); async Task AddDocumentsAndProject(Project?project, ImmutableArray <string> supportedLanguages, bool isOpen, CancellationToken cancellationToken) { if (project == null) { return; } if (!supportedLanguages.Contains(project.Language)) { // This project is for a language not supported by the LSP server making the request. // Do not report diagnostics for these projects. return; } var isFSAOn = globalOptions.IsFullSolutionAnalysisEnabled(project.Language); var documents = ImmutableArray <TextDocument> .Empty; // If FSA is on, then add all the documents in the project. Other analysis scopes are handled by the document pull handler. if (isFSAOn) { documents = documents.AddRange(project.Documents).AddRange(project.AdditionalDocuments); } // If all features are enabled for source generated documents, make sure they are included when FSA is on or a file in the project is open. // This is done because for either scenario we've already run generators, so there shouldn't be much cost in getting the diagnostics. if ((isFSAOn || isOpen) && solution.Services.GetService <IWorkspaceConfigurationService>()?.Options.EnableOpeningSourceGeneratedFiles == true) { var sourceGeneratedDocuments = await project.GetSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false); documents = documents.AddRange(sourceGeneratedDocuments); } foreach (var document in documents) { // Only consider closed documents here (and only open ones in the DocumentPullDiagnosticHandler). // Each handler treats those as separate worlds that they are responsible for. if (context.IsTracking(document.GetURI())) { context.TraceInformation($"Skipping tracked document: {document.GetURI()}"); continue; } // Do not attempt to get workspace diagnostics for Razor files, Razor will directly ask us for document diagnostics // for any razor file they are interested in. if (document.IsRazorDocument()) { continue; } result.Add(new WorkspaceDocumentDiagnosticSource(document)); } // Finally, if FSA is on we also want to check for diagnostics associated with the project itself. if (isFSAOn) { result.Add(new ProjectDiagnosticSource(project)); } } }