aspnetcore/src/Microsoft.CodeAnalysis.Razo.../ProjectSystem/ProjectState.cs

238 lines
7.5 KiB
C#

// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.CodeAnalysis.Razor.ProjectSystem
{
// Internal tracker for DefaultProjectSnapshot
internal class ProjectState
{
private static readonly IReadOnlyDictionary<string, DocumentState> EmptyDocuments = new Dictionary<string, DocumentState>();
private readonly object _lock;
private ProjectEngineTracker _projectEngine;
private ProjectTagHelperTracker _tagHelpers;
public ProjectState(
HostWorkspaceServices services,
HostProject hostProject,
Project workspaceProject)
{
if (services == null)
{
throw new ArgumentNullException(nameof(services));
}
if (hostProject == null)
{
throw new ArgumentNullException(nameof(hostProject));
}
Services = services;
HostProject = hostProject;
WorkspaceProject = workspaceProject;
Documents = EmptyDocuments;
Version = VersionStamp.Create();
_lock = new object();
}
public ProjectState(
ProjectState older,
ProjectDifference difference,
HostProject hostProject,
Project workspaceProject,
IReadOnlyDictionary<string, DocumentState> documents)
{
if (older == null)
{
throw new ArgumentNullException(nameof(older));
}
if (hostProject == null)
{
throw new ArgumentNullException(nameof(hostProject));
}
if (documents == null)
{
throw new ArgumentNullException(nameof(documents));
}
Services = older.Services;
Version = older.Version.GetNewerVersion();
HostProject = hostProject;
WorkspaceProject = workspaceProject;
Documents = documents;
_lock = new object();
_projectEngine = older._projectEngine?.ForkFor(this, difference);
_tagHelpers = older._tagHelpers?.ForkFor(this, difference);
}
public IReadOnlyDictionary<string, DocumentState> Documents { get; }
public HostProject HostProject { get; }
public HostWorkspaceServices Services { get; }
public Project WorkspaceProject { get; }
public VersionStamp Version { get; }
// Computed State
public ProjectEngineTracker ProjectEngine
{
get
{
if (_projectEngine == null)
{
lock (_lock)
{
if (_projectEngine == null)
{
_projectEngine = new ProjectEngineTracker(this);
}
}
}
return _projectEngine;
}
}
// Computed State
public ProjectTagHelperTracker TagHelpers
{
get
{
if (_tagHelpers == null)
{
lock (_lock)
{
if (_tagHelpers == null)
{
_tagHelpers = new ProjectTagHelperTracker(this);
}
}
}
return _tagHelpers;
}
}
public ProjectState AddHostDocument(HostDocument hostDocument)
{
if (hostDocument == null)
{
throw new ArgumentNullException(nameof(hostDocument));
}
// Ignore attempts to 'add' a document with different data, we only
// care about one, so it might as well be the one we have.
if (Documents.ContainsKey(hostDocument.FilePath))
{
return this;
}
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
foreach (var kvp in Documents)
{
documents.Add(kvp.Key, kvp.Value);
}
documents.Add(hostDocument.FilePath, new DocumentState(Services, hostDocument));
var difference = ProjectDifference.DocumentsChanged;
var state = new ProjectState(this, difference, HostProject, WorkspaceProject, documents);
return state;
}
public ProjectState RemoveHostDocument(HostDocument hostDocument)
{
if (hostDocument == null)
{
throw new ArgumentNullException(nameof(hostDocument));
}
if (!Documents.ContainsKey(hostDocument.FilePath))
{
return this;
}
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
foreach (var kvp in Documents)
{
documents.Add(kvp.Key, kvp.Value);
}
documents.Remove(hostDocument.FilePath);
var difference = ProjectDifference.DocumentsChanged;
var state = new ProjectState(this, difference, HostProject, WorkspaceProject, documents);
return state;
}
public ProjectState WithHostProject(HostProject hostProject)
{
if (hostProject == null)
{
throw new ArgumentNullException(nameof(hostProject));
}
if (HostProject.Configuration.Equals(hostProject.Configuration))
{
return this;
}
var difference = ProjectDifference.ConfigurationChanged;
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
foreach (var kvp in Documents)
{
documents.Add(kvp.Key, new DocumentState(kvp.Value, difference));
}
var state = new ProjectState(this, difference, hostProject, WorkspaceProject, documents);
return state;
}
public ProjectState WithWorkspaceProject(Project workspaceProject)
{
var difference = ProjectDifference.None;
if (WorkspaceProject == null && workspaceProject != null)
{
difference |= ProjectDifference.WorkspaceProjectAdded;
}
else if (WorkspaceProject != null && workspaceProject == null)
{
difference |= ProjectDifference.WorkspaceProjectRemoved;
}
else
{
// We always update the snapshot right now when the project changes. This is how
// we deal with changes to the content of C# sources.
difference |= ProjectDifference.WorkspaceProjectChanged;
}
if (difference == ProjectDifference.None)
{
return this;
}
var documents = new Dictionary<string, DocumentState>(FilePathComparer.Instance);
foreach (var kvp in Documents)
{
documents.Add(kvp.Key, new DocumentState(kvp.Value, difference));
}
var state = new ProjectState(this, difference, HostProject, workspaceProject, documents);
return state;
}
}
}