File: ProjectSystem\RemoteSolutionSnapshot.cs
Web Access
Project: src\src\Razor\src\Razor\src\Microsoft.CodeAnalysis.Remote.Razor\Microsoft.CodeAnalysis.Remote.Razor.csproj (Microsoft.CodeAnalysis.Remote.Razor)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.AspNetCore.Razor.PooledObjects;
using Microsoft.CodeAnalysis.Razor;
using Microsoft.CodeAnalysis.Razor.ProjectSystem;
 
namespace Microsoft.CodeAnalysis.Remote.Razor.ProjectSystem;
 
internal sealed class RemoteSolutionSnapshot(Solution solution, RemoteSnapshotManager snapshotManager) : ISolutionQueryOperations
{
    public RemoteSnapshotManager SnapshotManager { get; } = snapshotManager;
 
    private readonly Solution _solution = solution;
    private readonly Dictionary<Project, RemoteProjectSnapshot> _projectMap = [];
 
    public RemoteProjectSnapshot GetProject(ProjectId projectId)
    {
        var project = _solution.GetRequiredProject(projectId);
        return GetProject(project);
    }
 
    public RemoteProjectSnapshot GetProject(Project project)
    {
        if (project.Solution != _solution)
        {
            throw new ArgumentException(SR.Project_does_not_belong_to_this_solution, nameof(project));
        }
 
        if (!project.ContainsRazorDocuments())
        {
            throw new ArgumentException(SR.Project_does_not_contain_any_Razor_documents, nameof(project));
        }
 
        return GetProjectCore(project);
    }
 
    private RemoteProjectSnapshot GetProjectCore(Project project)
    {
        lock (_projectMap)
        {
            if (!_projectMap.TryGetValue(project, out var snapshot))
            {
                snapshot = new RemoteProjectSnapshot(project, this);
                _projectMap.Add(project, snapshot);
            }
 
            return snapshot;
        }
    }
 
    public IEnumerable<IProjectSnapshot> GetProjects()
        => _solution.Projects
            .Where(static p => p.ContainsRazorDocuments())
            .Select(GetProjectCore);
 
    public ImmutableArray<IProjectSnapshot> GetProjectsContainingDocument(string documentFilePath)
    {
        if (!documentFilePath.IsRazorFilePath())
        {
            throw new ArgumentException(SR.Format0_is_not_a_Razor_file_path(documentFilePath), nameof(documentFilePath));
        }
 
        var documentIds = _solution.GetDocumentIdsWithFilePath(documentFilePath);
 
        if (documentIds.IsEmpty)
        {
            return [];
        }
 
        using var results = new PooledArrayBuilder<IProjectSnapshot>(capacity: documentIds.Length);
        using var _ = HashSetPool<ProjectId>.GetPooledObject(out var projectIdSet);
 
        foreach (var documentId in documentIds)
        {
            var projectId = documentId.ProjectId;
 
            // We use a set to ensure that we only ever return the same project once.
            if (projectIdSet.Add(projectId))
            {
                // Since documentFilePath was proven to be a Razor file path, we know that
                // the projects will contain Razor documents and won't throw here.
                var project = _solution.GetRequiredProject(projectId);
                results.Add(GetProjectCore(project));
            }
        }
 
        return results.ToImmutableAndClear();
    }
}