File: Handler\SpellCheck\WorkspaceSpellCheckHandler.cs
Web Access
Project: src\src\LanguageServer\Protocol\Microsoft.CodeAnalysis.LanguageServer.Protocol.csproj (Microsoft.CodeAnalysis.LanguageServer.Protocol)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
 
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.ExternalAccess.Razor.Api;
using Microsoft.CodeAnalysis.PooledObjects;
using Roslyn.LanguageServer.Protocol;
using Roslyn.Utilities;
 
namespace Microsoft.CodeAnalysis.LanguageServer.Handler.SpellCheck;
 
[Method(VSInternalMethods.WorkspaceSpellCheckableRangesName)]
internal sealed class WorkspaceSpellCheckHandler : AbstractSpellCheckHandler<VSInternalWorkspaceSpellCheckableParams, VSInternalWorkspaceSpellCheckableReport>
{
    protected override VSInternalWorkspaceSpellCheckableReport CreateReport(TextDocumentIdentifier identifier, int[]? ranges, string? resultId)
        => new()
        {
            TextDocument = identifier,
            Ranges = ranges,
            ResultId = resultId,
        };
 
    public override TextDocumentIdentifier? GetTextDocumentIdentifier(VSInternalWorkspaceSpellCheckableParams requestParams) => null;
 
    protected override ImmutableArray<PreviousPullResult>? GetPreviousResults(VSInternalWorkspaceSpellCheckableParams requestParams)
        => requestParams.PreviousResults?.Where(d => d.PreviousResultId != null).Select(d => new PreviousPullResult(d.PreviousResultId!, d.TextDocument!)).ToImmutableArray();
 
    protected override ImmutableArray<Document> GetOrderedDocuments(RequestContext context, CancellationToken cancellationToken)
    {
        Contract.ThrowIfNull(context.Solution);
 
        using var _ = ArrayBuilder<Document>.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 spans for.  This will
        // prioritize the files from currently active projects, but then also include all other docs in all
        // projects.
 
        var activeDocument = documentTrackingService.GetActiveDocument(solution);
        var visibleDocuments = documentTrackingService.GetVisibleDocuments(solution);
 
        // Now, prioritize the projects related to the active/visible files.
        AddDocumentsFromProject(activeDocument?.Project, context.SupportedLanguages);
        foreach (var doc in visibleDocuments)
            AddDocumentsFromProject(doc.Project, context.SupportedLanguages);
 
        // finally, add the remainder of all documents.
        foreach (var project in solution.Projects)
            AddDocumentsFromProject(project, context.SupportedLanguages);
 
        // Ensure that we only process documents once.
        result.RemoveDuplicates();
        return result.ToImmutableAndClear();
 
        void AddDocumentsFromProject(Project? project, ImmutableArray<string> supportedLanguages)
        {
            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
                // spans for these projects.
                return;
            }
 
            var documents = project.Documents;
            foreach (var document in documents)
            {
                cancellationToken.ThrowIfCancellationRequested();
 
                // Only consider closed documents here (and only open ones in the DocumentSpellCheckingHandler).
                // 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 spell check results for Razor files, Razor will directly ask us for document based results
                // for any razor file they are interested in.
                if (document.IsRazorDocument())
                {
                    continue;
                }
 
                result.Add(document);
            }
        }
    }
}