File: CodeModel\ProjectCodeModel.cs
Web Access
Project: src\src\VisualStudio\Core\Impl\Microsoft.VisualStudio.LanguageServices.Implementation.csproj (Microsoft.VisualStudio.LanguageServices.Implementation)
// 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.
 
#nullable disable
 
using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Editor.Shared.Utilities;
using Microsoft.VisualStudio.LanguageServices.Implementation.Interop;
using Roslyn.Utilities;
 
namespace Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel;
 
/// <summary>
/// The root type that is held by a project to provide CodeModel support.
/// </summary>
internal sealed class ProjectCodeModel : IProjectCodeModel
{
    private readonly NonReentrantLock _guard = new NonReentrantLock();
    private readonly IThreadingContext _threadingContext;
    private readonly ProjectId _projectId;
    private readonly ICodeModelInstanceFactory _codeModelInstanceFactory;
    private readonly VisualStudioWorkspace _visualStudioWorkspace;
    private readonly IServiceProvider _serviceProvider;
    private readonly ProjectCodeModelFactory _projectCodeModelFactory;
 
    private CodeModelProjectCache _codeModelCache;
 
    public ProjectCodeModel(
        IThreadingContext threadingContext,
        ProjectId projectId,
        ICodeModelInstanceFactory codeModelInstanceFactory,
        VisualStudioWorkspace visualStudioWorkspace,
        IServiceProvider serviceProvider,
        ProjectCodeModelFactory projectCodeModelFactory)
    {
        _threadingContext = threadingContext;
        _projectId = projectId;
        _codeModelInstanceFactory = codeModelInstanceFactory;
        _visualStudioWorkspace = visualStudioWorkspace;
        _serviceProvider = serviceProvider;
        _projectCodeModelFactory = projectCodeModelFactory;
    }
 
    public void OnProjectClosed()
    {
        _codeModelCache?.OnProjectClosed();
        _projectCodeModelFactory.OnProjectClosed(_projectId);
    }
 
    private CodeModelProjectCache GetCodeModelCache()
    {
        Contract.ThrowIfNull(_projectId);
        Contract.ThrowIfNull(_visualStudioWorkspace);
 
        using (_guard.DisposableWait())
        {
            if (_codeModelCache == null)
            {
                var workspaceProject = _visualStudioWorkspace.CurrentSolution.GetProject(_projectId);
 
                if (workspaceProject != null)
                {
                    _codeModelCache = new CodeModelProjectCache(_threadingContext, _projectId, _codeModelInstanceFactory, _projectCodeModelFactory, _serviceProvider, workspaceProject.Services, _visualStudioWorkspace);
                }
            }
 
            return _codeModelCache;
        }
    }
 
    internal IEnumerable<ComHandle<EnvDTE80.FileCodeModel2, FileCodeModel>> GetCachedFileCodeModelInstances()
        => GetCodeModelCache().GetFileCodeModelInstances();
 
    internal bool TryGetCachedFileCodeModel(string fileName, out ComHandle<EnvDTE80.FileCodeModel2, FileCodeModel> fileCodeModelHandle)
    {
        var handle = GetCodeModelCache()?.GetComHandleForFileCodeModel(fileName);
 
        fileCodeModelHandle = handle != null
            ? handle.Value
            : default;
 
        return handle != null;
    }
 
    /// <summary>
    /// Gets or creates a <see cref="FileCodeModel"/> for the given file name. Because we don't have
    /// a parent object, this will call back to the project system to provide us the parent object.
    /// </summary>
    public ComHandle<EnvDTE80.FileCodeModel2, FileCodeModel> GetOrCreateFileCodeModel(string filePath)
        => GetCodeModelCache().GetOrCreateFileCodeModel(filePath);
 
    public ComHandle<EnvDTE80.FileCodeModel2, FileCodeModel> GetOrCreateFileCodeModel(string filePath, object parent)
        => GetCodeModelCache().GetOrCreateFileCodeModel(filePath, parent);
 
    public EnvDTE.CodeModel GetOrCreateRootCodeModel(EnvDTE.Project parent)
        => GetCodeModelCache().GetOrCreateRootCodeModel(parent);
 
    public void OnSourceFileRemoved(string fileName)
    {
        // This uses the field directly. If we haven't yet created the CodeModelProjectCache, then we most definitely
        // don't have any source files we need to zombie when they go away. There's no reason to create a cache in that case.
        _codeModelCache?.OnSourceFileRemoved(fileName);
    }
 
    public void OnSourceFileRenaming(string filePath, string newFilePath)
    {
        // This uses the field directly. If we haven't yet created the CodeModelProjectCache, then we most definitely
        // don't have any source files we need to handle a rename for. There's no reason to create a cache in that case.
        _codeModelCache?.OnSourceFileRenaming(filePath, newFilePath);
    }
 
    EnvDTE.FileCodeModel IProjectCodeModel.GetOrCreateFileCodeModel(string filePath, object parent)
        => this.GetOrCreateFileCodeModel(filePath, parent).Handle;
 
    public EnvDTE.FileCodeModel CreateFileCodeModel(SourceGeneratedDocument sourceGeneratedDocument)
    {
        // Unlike for "regular" documents, we make no effort to cache these between callers or hold them for longer lifetimes with
        // events.
        return FileCodeModel.Create(GetCodeModelCache().State, parent: null, sourceGeneratedDocument.Id, isSourceGeneratorOutput: true, new TextManagerAdapter()).Handle;
    }
}