提交 20be61ac 编写于 作者: D dpoeschl

Move linked file tracking from the Workspace into the Solution and other small...

Move linked file tracking from the Workspace into the Solution and other small linked file bug fixes. (changeset 1234227)
上级 46485bb3
......@@ -14,7 +14,7 @@ internal class LinkedFileReferenceFinder : IReferenceFinder
{
public async Task<IEnumerable<ISymbol>> DetermineCascadedSymbolsAsync(ISymbol symbol, Solution solution, IImmutableSet<Project> projects = null, CancellationToken cancellationToken = default(CancellationToken))
{
var linkedSymbols = new List<ISymbol>();
var linkedSymbols = new HashSet<ISymbol>();
foreach (var location in symbol.DeclaringSyntaxReferences)
{
......@@ -29,10 +29,10 @@ public async Task<IEnumerable<ISymbol>> DetermineCascadedSymbolsAsync(ISymbol sy
var semanticModel = await linkedDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var linkedSymbol = semanticModel.GetDeclaredSymbol(linkedNode, cancellationToken);
if (linkedSymbol != null &&
linkedSymbol != symbol &&
if (linkedSymbol != null &&
linkedSymbol.Kind == symbol.Kind &&
linkedSymbol.Name == symbol.Name)
linkedSymbol.Name == symbol.Name &&
!linkedSymbols.Contains(linkedSymbol))
{
linkedSymbols.Add(linkedSymbol);
}
......
......@@ -143,9 +143,9 @@ public static async Task<bool> IsForkedDocumentWithSyntaxChangesAsync(this Docum
/// </summary>
public static IEnumerable<DocumentId> GetLinkedDocumentIds(this Document document)
{
return document.Project.Solution.Workspace
.GetDocumentIdsWithPath(document.FilePath)
.Where(documentId => documentId != document.Id && document.Project.Solution.ContainsDocument(documentId));
return document.Project.Solution
.GetDocumentIdsWithFilePath(document.FilePath)
.Where(documentId => documentId != document.Id);
}
}
}
\ No newline at end of file
......@@ -34,6 +34,7 @@ public partial class Solution
private readonly string filePath;
private readonly ImmutableList<ProjectId> projectIds;
private readonly ImmutableDictionary<ProjectId, ProjectState> projectIdToProjectStateMap;
private readonly ImmutableDictionary<string, ImmutableList<DocumentId>> linkedFilesMap;
private readonly VersionStamp version;
private readonly Lazy<VersionStamp> lazyLatestProjectVersion;
private readonly ProjectDependencyGraph dependencyGraph;
......@@ -51,6 +52,7 @@ public partial class Solution
ImmutableList<ProjectId> projectIds,
ImmutableDictionary<ProjectId, ProjectState> idToProjectStateMap,
ImmutableDictionary<ProjectId, CompilationTracker> projectIdToTrackerMap,
ImmutableDictionary<string, ImmutableList<DocumentId>> linkedFilesMap,
ProjectDependencyGraph dependencyGraph,
VersionStamp version,
Lazy<VersionStamp> lazyLatestProjectVersion)
......@@ -63,6 +65,7 @@ public partial class Solution
this.projectIds = projectIds;
this.projectIdToProjectStateMap = idToProjectStateMap;
this.projectIdToTrackerMap = projectIdToTrackerMap;
this.linkedFilesMap = linkedFilesMap;
this.dependencyGraph = dependencyGraph;
this.projectIdToProjectMap = ImmutableHashMap<ProjectId, Project>.Empty;
this.version = version;
......@@ -84,6 +87,7 @@ public partial class Solution
projectIds: ImmutableList.Create<ProjectId>(),
idToProjectStateMap: ImmutableDictionary<ProjectId, ProjectState>.Empty,
projectIdToTrackerMap: ImmutableDictionary<ProjectId, CompilationTracker>.Empty,
linkedFilesMap: ImmutableDictionary.Create<string, ImmutableList<DocumentId>>(StringComparer.OrdinalIgnoreCase),
dependencyGraph: ProjectDependencyGraph.Empty,
lazyLatestProjectVersion: null)
{
......@@ -201,6 +205,7 @@ private void CheckInvariants()
ImmutableList<ProjectId> projectIds = null,
ImmutableDictionary<ProjectId, ProjectState> idToProjectStateMap = null,
ImmutableDictionary<ProjectId, CompilationTracker> projectIdToTrackerMap = null,
ImmutableDictionary<string, ImmutableList<DocumentId>> linkedFilesMap = null,
ProjectDependencyGraph dependencyGraph = null,
VersionStamp? version = default(VersionStamp?),
Lazy<VersionStamp> lazyLatestProjectVersion = null)
......@@ -210,6 +215,7 @@ private void CheckInvariants()
projectIds = projectIds ?? this.projectIds;
idToProjectStateMap = idToProjectStateMap ?? this.projectIdToProjectStateMap;
projectIdToTrackerMap = projectIdToTrackerMap ?? this.projectIdToTrackerMap;
linkedFilesMap = linkedFilesMap ?? this.linkedFilesMap;
dependencyGraph = dependencyGraph ?? this.dependencyGraph;
version = version.HasValue ? version.Value : this.version;
lazyLatestProjectVersion = lazyLatestProjectVersion ?? this.lazyLatestProjectVersion;
......@@ -218,6 +224,7 @@ private void CheckInvariants()
projectIds == this.projectIds &&
idToProjectStateMap == this.projectIdToProjectStateMap &&
projectIdToTrackerMap == this.projectIdToTrackerMap &&
linkedFilesMap == this.linkedFilesMap &&
dependencyGraph == this.dependencyGraph &&
version == this.version &&
lazyLatestProjectVersion == this.lazyLatestProjectVersion)
......@@ -234,6 +241,7 @@ private void CheckInvariants()
projectIds,
idToProjectStateMap,
projectIdToTrackerMap,
linkedFilesMap,
dependencyGraph,
version.Value,
lazyLatestProjectVersion);
......@@ -260,6 +268,7 @@ private void CheckInvariants()
this.projectIds,
this.projectIdToProjectStateMap,
this.projectIdToTrackerMap,
this.linkedFilesMap,
this.dependencyGraph,
this.version,
this.lazyLatestProjectVersion);
......@@ -463,7 +472,7 @@ internal Task<VersionStamp> GetDependentSemanticVersionAsync(ProjectId projectId
}
internal ProjectState GetProjectState(ProjectId projectId)
{
{
ProjectState state;
this.projectIdToProjectStateMap.TryGetValue(projectId, out state);
return state;
......@@ -497,11 +506,13 @@ private Solution AddProject(ProjectId projectId, ProjectState projectState)
var newStateMap = this.projectIdToProjectStateMap.Add(projectId, projectState);
var newDependencyGraph = CreateDependencyGraph(newProjectIds, newStateMap);
var newTrackerMap = CreateCompilationTrackerMap(projectId, newDependencyGraph);
var newLinkedFilesMap = CreateLinkedFilesMapWithAddedProject(newStateMap[projectId]);
return this.Branch(
projectIds: newProjectIds,
idToProjectStateMap: newStateMap,
projectIdToTrackerMap: newTrackerMap,
linkedFilesMap: newLinkedFilesMap,
dependencyGraph: newDependencyGraph,
version: this.Version.GetNewerVersion(), // changed project list so, increment version.
lazyLatestProjectVersion: new Lazy<VersionStamp>(() => projectState.Version)); // this is the newest!
......@@ -562,20 +573,31 @@ public Solution AddProject(ProjectInfo projectInfo)
return this.AddProject(newProject.Id, newProject);
}
private static Exception UnwrapException(Exception e)
private ImmutableDictionary<string, ImmutableList<DocumentId>> CreateLinkedFilesMapWithAddedProject(ProjectState projectState)
{
return CreateLinkedFilesMapWithAddedDocuments(projectState, projectState.DocumentIds);
}
private ImmutableDictionary<string, ImmutableList<DocumentId>> CreateLinkedFilesMapWithAddedDocuments(ProjectState projectState, ImmutableList<DocumentId> documentIds)
{
while (true)
var builder = this.linkedFilesMap.ToBuilder();
foreach (var documentId in documentIds)
{
var agg = e as AggregateException;
if (agg == null || agg.InnerExceptions.Count != 1)
{
return e;
}
else
var filePath = projectState.GetDocumentState(documentId).FilePath;
if (string.IsNullOrWhiteSpace(filePath))
{
e = agg.InnerExceptions[0];
continue;
}
ImmutableList<DocumentId> documentIdsWithPath;
builder[filePath] = builder.TryGetValue(filePath, out documentIdsWithPath)
? documentIdsWithPath.Add(documentId)
: ImmutableList.Create(documentId);
}
return builder.ToImmutable();
}
/// <summary>
......@@ -594,15 +616,56 @@ public Solution RemoveProject(ProjectId projectId)
var newStateMap = this.projectIdToProjectStateMap.Remove(projectId);
var newDependencyGraph = CreateDependencyGraph(newProjectIds, newStateMap);
var newTrackerMap = CreateCompilationTrackerMap(projectId, newDependencyGraph);
var newLinkedFilesMap = CreateLinkedFilesMapWithRemovedProject(this.projectIdToProjectStateMap[projectId]);
return this.Branch(
projectIds: newProjectIds,
idToProjectStateMap: newStateMap,
projectIdToTrackerMap: newTrackerMap.Remove(projectId),
linkedFilesMap: newLinkedFilesMap,
dependencyGraph: newDependencyGraph,
version: this.Version.GetNewerVersion()); // changed project list, so increment version
}
private ImmutableDictionary<string, ImmutableList<DocumentId>> CreateLinkedFilesMapWithRemovedProject(ProjectState projectState)
{
return CreateLinkedFilesMapWithRemovedDocuments(projectState, projectState.DocumentIds);
}
private ImmutableDictionary<string, ImmutableList<DocumentId>> CreateLinkedFilesMapWithRemovedDocuments(
ProjectState projectState,
ImmutableList<DocumentId> documentIds)
{
var builder = this.linkedFilesMap.ToBuilder();
foreach (var documentId in documentIds)
{
var filePath = projectState.GetDocumentState(documentId).FilePath;
if (string.IsNullOrWhiteSpace(filePath))
{
continue;
}
ImmutableList<DocumentId> documentIdsWithPath;
if (!builder.TryGetValue(filePath, out documentIdsWithPath) || !documentIdsWithPath.Contains(documentId))
{
throw new ArgumentException("The given documentId was not found in the linkedFilesMap.");
}
if (documentIdsWithPath.Count == 1)
{
builder.Remove(filePath);
}
else
{
builder[filePath] = documentIdsWithPath.Remove(documentId);
}
}
return builder.ToImmutable();
}
/// <summary>
/// Creates a new solution instance with the project specified updated to have the new
/// assembly name.
......@@ -1000,7 +1063,7 @@ private Solution AddDocument(DocumentState state)
var oldProject = this.GetProjectState(state.Id.ProjectId);
var newProject = oldProject.AddDocument(state);
return this.ForkProject(newProject, CompilationTranslationAction.AddDocument(state));
return this.ForkProject(newProject, CompilationTranslationAction.AddDocument(state), withDocumentListChange: true);
}
/// <summary>
......@@ -1139,22 +1202,7 @@ public Solution RemoveDocument(DocumentId documentId)
var oldDocument = oldProject.GetDocumentState(documentId);
var newProject = oldProject.RemoveDocument(documentId);
return this.ForkProject(newProject, CompilationTranslationAction.RemoveDocument(oldDocument));
}
private Solution RemoveAllDocuments(ProjectId projectId)
{
CheckContainsProject(projectId);
var oldProject = this.GetProjectState(projectId);
if (!oldProject.HasDocuments)
{
return this;
}
var newProject = oldProject.RemoveAllDocuments();
return this.ForkProject(newProject, CompilationTranslationAction.RemoveAllDocuments());
return this.ForkProject(newProject, CompilationTranslationAction.RemoveDocument(oldDocument), withDocumentListChange: true);
}
/// <summary>
......@@ -1299,25 +1347,6 @@ public Solution WithDocumentSyntaxRoot(DocumentId documentId, SyntaxNode root, P
return WithDocumentState(oldDocument.UpdateTree(root, mode), textChanged: true);
}
private Solution WithDocumentState(Document document)
{
if (document == null)
{
throw new ArgumentNullException("document");
}
CheckContainsDocument(document.Id);
var oldDocumentState = this.GetDocumentState(document.Id);
var newDocumentState = document.State;
if (oldDocumentState == newDocumentState)
{
return this;
}
return WithDocumentState(newDocumentState);
}
private static async Task<Compilation> UpdateDocumentInCompilationAsync(
Compilation compilation,
DocumentState oldDocument,
......@@ -1413,7 +1442,8 @@ private Solution TouchDocument(DocumentId documentId, Func<ProjectState, Project
private Solution ForkProject(
ProjectState newProjectState,
CompilationTranslationAction translate = null,
bool withProjectReferenceChange = false)
bool withProjectReferenceChange = false,
bool withDocumentListChange = false)
{
// make sure we are getting only known translate actions
CompilationTranslationAction.CheckKnownActions(translate);
......@@ -1423,6 +1453,9 @@ private Solution TouchDocument(DocumentId documentId, Func<ProjectState, Project
var newStateMap = this.projectIdToProjectStateMap.SetItem(projectId, newProjectState);
var newDependencyGraph = withProjectReferenceChange ? CreateDependencyGraph(this.projectIds, newStateMap) : this.dependencyGraph;
var newTrackerMap = CreateCompilationTrackerMap(projectId, newDependencyGraph);
var newLinkedFilesMap = withDocumentListChange
? CreateLinkedFilesMapWithChangedProject(projectIdToProjectStateMap[projectId], newProjectState)
: this.linkedFilesMap;
// If we have a tracker for this project, then fork it as well (along with the
// translation action and store it in the tracker map.
......@@ -1439,9 +1472,41 @@ private Solution TouchDocument(DocumentId documentId, Func<ProjectState, Project
idToProjectStateMap: newStateMap,
projectIdToTrackerMap: newTrackerMap,
dependencyGraph: newDependencyGraph,
linkedFilesMap: newLinkedFilesMap,
lazyLatestProjectVersion: newLatestProjectVersion);
}
private ImmutableDictionary<string, ImmutableList<DocumentId>> CreateLinkedFilesMapWithChangedProject(ProjectState oldProjectState, ProjectState newProjectState)
{
var oldDocumentIds = oldProjectState.DocumentIds;
var newDocumentIds = newProjectState.DocumentIds;
var addedDocumentIds = newDocumentIds.RemoveRange(oldDocumentIds);
var removedDocumentIds = oldDocumentIds.RemoveRange(newDocumentIds);
Debug.Assert(addedDocumentIds.Any() || removedDocumentIds.Any(), "The solution's linkedFilesMap should only be recalculated if its files changed.");
var linkedFilesMap = this.linkedFilesMap;
linkedFilesMap = addedDocumentIds.Any() ? CreateLinkedFilesMapWithAddedDocuments(newProjectState, addedDocumentIds) : linkedFilesMap;
linkedFilesMap = removedDocumentIds.Any() ? CreateLinkedFilesMapWithRemovedDocuments(oldProjectState, removedDocumentIds) : linkedFilesMap;
return linkedFilesMap;
}
/// <summary>
/// Gets the set of documents in the current solution with the given file path.
/// </summary>
internal ImmutableList<DocumentId> GetDocumentIdsWithFilePath(string filePath)
{
if (string.IsNullOrWhiteSpace(filePath))
{
return ImmutableList<DocumentId>.Empty;
}
ImmutableList<DocumentId> documentIds;
return this.linkedFilesMap.TryGetValue(filePath, out documentIds)
? documentIds
: ImmutableList<DocumentId>.Empty;
}
private static ProjectDependencyGraph CreateDependencyGraph(
ImmutableList<ProjectId> projectIds,
ImmutableDictionary<ProjectId, ProjectState> projectStates)
......
......@@ -39,8 +39,6 @@ public abstract partial class Workspace : IDisposable
private readonly IWorkspaceTaskScheduler taskQueue;
private ImmutableDictionary<string, ImmutableList<DocumentId>> linkedFilesMap = ImmutableDictionary.Create<string, ImmutableList<DocumentId>>();
// test hooks.
internal static bool TestHookStandaloneProjectsDoNotHoldReferences = false;
......@@ -363,8 +361,6 @@ private void OnProjectAdded_NoLock(ProjectInfo projectInfo, bool silent)
var oldSolution = this.CurrentSolution;
var newSolution = this.SetCurrentSolution(oldSolution.AddProject(projectInfo));
AddDocumentsToLinkedFilesMap_NoLock(newSolution, projectInfo.Id);
if (!silent)
{
this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.ProjectAdded, oldSolution, newSolution, projectId);
......@@ -404,8 +400,6 @@ protected internal virtual void OnProjectRemoved(ProjectId projectId)
var oldSolution = this.CurrentSolution;
RemoveDocumentsFromLinkedFilesMap_NoLock(oldSolution, projectId);
this.ClearProjectData(projectId);
var newSolution = this.SetCurrentSolution(oldSolution.RemoveProject(projectId));
......@@ -601,8 +595,6 @@ protected internal void OnDocumentAdded(DocumentInfo documentInfo)
CheckProjectIsInCurrentSolution(documentId.ProjectId);
CheckDocumentIsNotInCurrentSolution(documentId);
AddDocumentToLinkedFilesMap_NoLock(documentInfo.Id, documentInfo.FilePath);
var oldSolution = this.CurrentSolution;
var newSolution = this.SetCurrentSolution(oldSolution.AddDocument(documentInfo));
......@@ -610,39 +602,6 @@ protected internal void OnDocumentAdded(DocumentInfo documentInfo)
}
}
private void AddDocumentsToLinkedFilesMap_NoLock(Solution solution, ProjectId projectId)
{
var builder = linkedFilesMap.ToBuilder();
foreach (var document in solution.GetProject(projectId).Documents)
{
if (string.IsNullOrWhiteSpace(document.FilePath))
{
continue;
}
ImmutableList<DocumentId> documentIds;
builder[document.FilePath] = builder.TryGetValue(document.FilePath, out documentIds)
? documentIds.Add(document.Id)
: ImmutableList.Create(document.Id);
}
linkedFilesMap = builder.ToImmutable();
}
private void AddDocumentToLinkedFilesMap_NoLock(DocumentId documentId, string filePath)
{
if (string.IsNullOrWhiteSpace(filePath))
{
return;
}
ImmutableList<DocumentId> documentIds;
linkedFilesMap = linkedFilesMap.TryGetValue(filePath, out documentIds)
? linkedFilesMap.SetItem(filePath, documentIds.Add(documentId))
: linkedFilesMap.Add(filePath, ImmutableList.Create(documentId));
}
/// <summary>
/// Call this method when a document is reloaded in the host environment.
/// </summary>
......@@ -675,8 +634,6 @@ protected internal void OnDocumentRemoved(DocumentId documentId)
var oldSolution = this.CurrentSolution;
RemoveDocumentFromLinkedFilesMap_NoLock(documentId);
this.ClearDocumentData(documentId);
var newSolution = this.SetCurrentSolution(oldSolution.RemoveDocument(documentId));
......@@ -685,79 +642,11 @@ protected internal void OnDocumentRemoved(DocumentId documentId)
}
}
private void RemoveDocumentsFromLinkedFilesMap_NoLock(Solution solution, ProjectId projectId)
{
var builder = linkedFilesMap.ToBuilder();
foreach (var document in solution.GetProject(projectId).Documents)
{
if (string.IsNullOrWhiteSpace(document.FilePath))
{
continue;
}
ImmutableList<DocumentId> documentIds;
if (!builder.TryGetValue(document.FilePath, out documentIds) || !documentIds.Contains(document.Id))
{
throw new ArgumentException("The given documentId was not found in the linkedFilesMap.");
}
if (documentIds.Count == 1)
{
builder.Remove(document.FilePath);
}
else
{
builder[document.FilePath] = documentIds.Remove(document.Id);
}
}
linkedFilesMap = builder.ToImmutable();
}
private void RemoveDocumentFromLinkedFilesMap_NoLock(DocumentId documentId)
{
var path = this.CurrentSolution.GetDocument(documentId).FilePath;
if (string.IsNullOrWhiteSpace(path))
{
return;
}
ImmutableList<DocumentId> documentIds;
if (!linkedFilesMap.TryGetValue(path, out documentIds) || !documentIds.Contains(documentId))
{
throw new ArgumentException("The given documentId was not found in the linkedFilesMap.");
}
linkedFilesMap = documentIds.Count == 1
? linkedFilesMap.Remove(path)
: linkedFilesMap.SetItem(path, documentIds.Remove(documentId));
}
protected virtual void CheckDocumentCanBeRemoved(DocumentId documentId)
{
CheckDocumentIsClosed(documentId);
}
/// <summary>
/// Gets the set of documents in the current solution with the given file path.
/// </summary>
internal ImmutableList<DocumentId> GetDocumentIdsWithPath(string filePath)
{
if (string.IsNullOrWhiteSpace(filePath))
{
return ImmutableList<DocumentId>.Empty;
}
using (this.serializationLock.DisposableWait())
{
ImmutableList<DocumentId> documentIds;
return linkedFilesMap.TryGetValue(filePath, out documentIds)
? documentIds
: ImmutableList<DocumentId>.Empty;
}
}
/// <summary>
/// Call this method when the text of a document is changed on disk.
/// </summary>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册