public async ValueTask MergeAsync(ActiveFileState state, TextDocument document, IGlobalOptionService globalOptions) { Contract.ThrowIfFalse(state.DocumentId == document.Id); // merge active file state to project state var lastResult = _lastResult; var syntax = state.GetAnalysisData(AnalysisKind.Syntax); var semantic = state.GetAnalysisData(AnalysisKind.Semantic); var project = document.Project; // if project didn't successfully loaded, then it is same as FSA off var fullAnalysis = globalOptions.GetBackgroundAnalysisScope(project.Language) == BackgroundAnalysisScope.FullSolution && await project.HasSuccessfullyLoadedAsync(CancellationToken.None).ConfigureAwait(false); // keep from build flag if full analysis is off var fromBuild = fullAnalysis ? false : lastResult.FromBuild; var languageServices = document.Project.LanguageServices; var simplifierOptions = (languageServices.GetService <ISimplifierOptionsStorage>() != null) ? globalOptions.GetSimplifierOptions(languageServices) : null; var openFileOnlyAnalyzer = _owner.Analyzer.IsOpenFileOnly(simplifierOptions); // if it is allowed to keep project state, check versions and if they are same, bail out. // if full solution analysis is off or we are asked to reset document state, we always merge. if (fullAnalysis && !openFileOnlyAnalyzer && syntax.Version != VersionStamp.Default && syntax.Version == semantic.Version && syntax.Version == lastResult.Version) { // all data is in sync already. return; } // we have mixed versions or full analysis is off, set it to default so that it can be re-calculated next time so data can be in sync. var version = VersionStamp.Default; // serialization can't be canceled. var serializerVersion = version; // save active file diagnostics back to project state await AddToInMemoryStorageAsync(serializerVersion, project, document, document.Id, _owner.SyntaxStateName, syntax.Items).ConfigureAwait(false); await AddToInMemoryStorageAsync(serializerVersion, project, document, document.Id, _owner.SemanticStateName, semantic.Items).ConfigureAwait(false); // save last aggregated form of analysis result _lastResult = _lastResult.UpdateAggregatedResult(version, state.DocumentId, fromBuild); }
internal static async ValueTask <ImmutableArray <Document> > 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.ClientName != null) { return(ImmutableArray <Document> .Empty); } using var _ = ArrayBuilder <Document> .GetInstance(out var result); var solution = context.Solution; var documentTrackingService = solution.Workspace.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 AddDocumentsFromProjectAsync(activeDocument?.Project, context.SupportedLanguages, isOpen : true, cancellationToken).ConfigureAwait(false); foreach (var doc in visibleDocuments) { await AddDocumentsFromProjectAsync(doc.Project, context.SupportedLanguages, isOpen : true, cancellationToken).ConfigureAwait(false); } // finally, add the remainder of all documents. foreach (var project in solution.Projects) { await AddDocumentsFromProjectAsync(project, context.SupportedLanguages, isOpen : false, cancellationToken).ConfigureAwait(false); } // Ensure that we only process documents once. result.RemoveDuplicates(); return(result.ToImmutable()); async Task AddDocumentsFromProjectAsync(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; } // if the project doesn't necessarily have an open file in it, then only include it if the user has full // solution analysis on. if (!isOpen) { if (globalOptions.GetBackgroundAnalysisScope(project.Language) != BackgroundAnalysisScope.FullSolution) { context.TraceInformation($"Skipping project '{project.Name}' as it has no open document and Full Solution Analysis is off"); return; } } // Otherwise, if the user has an open file from this project, or FSA is on, then include all the // documents from it. If all features are enabled for source generated documents, make sure they are // included as well. var documents = project.Documents; if (solution.Workspace.Services.GetService <ISyntaxTreeConfigurationService>() is { EnableOpeningSourceGeneratedFilesInWorkspace : true }) { documents = documents.Concat(await project.GetSourceGeneratedDocumentsAsync(cancellationToken).ConfigureAwait(false)); } 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; } result.Add(document); } }