提交 b894bb95 编写于 作者: J Jason Malinowski

Synchronize AnalyzerConfig documents when creating OOP solutions

上级 16001f42
......@@ -408,6 +408,9 @@ public async Task<VersionStamp> GetSemanticVersionAsync(CancellationToken cancel
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
public IImmutableDictionary<DocumentId, TextDocumentState> AdditionalDocumentStates => _additionalDocumentStates;
[DebuggerBrowsable(DebuggerBrowsableState.Collapsed)]
public IImmutableDictionary<DocumentId, AnalyzerConfigDocumentState> AnalyzerConfigDocumentStates => _analyzerConfigDocumentStates;
public bool ContainsDocument(DocumentId documentId)
{
return _documentStates.ContainsKey(documentId);
......
......@@ -111,6 +111,7 @@ public ProjectStateChecksums(params object[] children) : base(WellKnownSynchroni
public AnalyzerReferenceChecksumCollection AnalyzerReferences => (AnalyzerReferenceChecksumCollection)Children[6];
public TextDocumentChecksumCollection AdditionalDocuments => (TextDocumentChecksumCollection)Children[7];
public AnalyzerConfigDocumentChecksumCollection AnalyzerConfigDocuments => (AnalyzerConfigDocumentChecksumCollection)Children[8];
public void Find(
ProjectState state,
......@@ -168,11 +169,17 @@ public ProjectStateChecksums(params object[] children) : base(WellKnownSynchroni
result[AdditionalDocuments.Checksum] = AdditionalDocuments;
}
if (searchingChecksumsLeft.Remove(AnalyzerConfigDocuments.Checksum))
{
result[AnalyzerConfigDocuments.Checksum] = AnalyzerConfigDocuments;
}
Find(state.DocumentStates, searchingChecksumsLeft, result, cancellationToken);
Find(state.ProjectReferences, ProjectReferences, searchingChecksumsLeft, result, cancellationToken);
Find(state.MetadataReferences, MetadataReferences, searchingChecksumsLeft, result, cancellationToken);
Find(state.AnalyzerReferences, AnalyzerReferences, searchingChecksumsLeft, result, cancellationToken);
Find(state.AdditionalDocumentStates, searchingChecksumsLeft, result, cancellationToken);
Find(state.AnalyzerConfigDocumentStates, searchingChecksumsLeft, result, cancellationToken);
}
private static void Find<T>(
......
......@@ -732,24 +732,32 @@ private async Task<Solution> GetSolutionAsync(IRemotableDataService service, Pin
continue;
}
var documents = new List<DocumentInfo>();
foreach (var documentObject in projectObject.Documents.ToDocumentObjects(service))
async Task<List<DocumentInfo>> CreateDocumentInfosAsync(ChecksumObjectCollection<DocumentStateChecksums> checksums)
{
var documentInfo = await service.GetValueAsync<DocumentInfo.DocumentAttributes>(documentObject.Info).ConfigureAwait(false);
var text = await service.GetValueAsync<SourceText>(documentObject.Text).ConfigureAwait(false);
// TODO: do we need version?
documents.Add(
DocumentInfo.Create(
documentInfo.Id,
documentInfo.Name,
documentInfo.Folders,
documentInfo.SourceCodeKind,
TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create())),
documentInfo.FilePath,
documentInfo.IsGenerated));
List<DocumentInfo> infos = new List<DocumentInfo>();
foreach (var documentStateChecksums in checksums)
{
var documentInfo = await service.GetValueAsync<DocumentInfo.DocumentAttributes>(documentStateChecksums.Info).ConfigureAwait(false);
var text = await service.GetValueAsync<SourceText>(documentStateChecksums.Text).ConfigureAwait(false);
// TODO: do we need version?
infos.Add(
DocumentInfo.Create(
documentInfo.Id,
documentInfo.Name,
documentInfo.Folders,
documentInfo.SourceCodeKind,
TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create())),
documentInfo.FilePath,
documentInfo.IsGenerated));
}
return infos;
}
var documents = await CreateDocumentInfosAsync(projectObject.Documents.ToDocumentObjects(service));
var p2p = new List<ProjectReference>();
foreach (var checksum in projectObject.ProjectReferences)
{
......@@ -771,23 +779,8 @@ private async Task<Solution> GetSolutionAsync(IRemotableDataService service, Pin
analyzers.Add(reference);
}
var additionals = new List<DocumentInfo>();
foreach (var documentObject in projectObject.AdditionalDocuments.ToDocumentObjects(service))
{
var documentInfo = await service.GetValueAsync<DocumentInfo.DocumentAttributes>(documentObject.Info).ConfigureAwait(false);
var text = await service.GetValueAsync<SourceText>(documentObject.Text).ConfigureAwait(false);
// TODO: do we need version?
additionals.Add(
DocumentInfo.Create(
documentInfo.Id,
documentInfo.Name,
documentInfo.Folders,
documentInfo.SourceCodeKind,
TextLoader.From(TextAndVersion.Create(text, VersionStamp.Create())),
documentInfo.FilePath,
documentInfo.IsGenerated));
}
var additionalDocuments = await CreateDocumentInfosAsync(projectObject.AdditionalDocuments.ToDocumentObjects(service));
var analyzerConfigDocuments = await CreateDocumentInfosAsync(projectObject.AnalyzerConfigDocuments.ToDocumentObjects(service));
var compilationOptions = await service.GetValueAsync<CompilationOptions>(projectObject.CompilationOptions).ConfigureAwait(false);
var parseOptions = await service.GetValueAsync<ParseOptions>(projectObject.ParseOptions).ConfigureAwait(false);
......@@ -797,7 +790,8 @@ private async Task<Solution> GetSolutionAsync(IRemotableDataService service, Pin
projectInfo.Id, projectInfo.Version, projectInfo.Name, projectInfo.AssemblyName,
projectInfo.Language, projectInfo.FilePath, projectInfo.OutputFilePath,
compilationOptions, parseOptions,
documents, p2p, metadata, analyzers, additionals, projectInfo.IsSubmission));
documents, p2p, metadata, analyzers, additionalDocuments, projectInfo.IsSubmission)
.WithAnalyzerConfigDocuments(analyzerConfigDocuments));
}
return workspace.AddSolution(SolutionInfo.Create(solutionInfo.Id, solutionInfo.Version, solutionInfo.FilePath, projects));
......
......@@ -50,6 +50,11 @@ public static ChecksumObjectCollection<DocumentStateChecksums> ToDocumentObjects
{
return new ChecksumObjectCollection<DocumentStateChecksums>(service, collection);
}
public static ChecksumObjectCollection<DocumentStateChecksums> ToDocumentObjects(this AnalyzerConfigDocumentChecksumCollection collection, IRemotableDataService service)
{
return new ChecksumObjectCollection<DocumentStateChecksums>(service, collection);
}
}
/// <summary>
......
......@@ -34,8 +34,14 @@ internal static Solution CreateFullSolution(HostServices hostServices = null)
project1 = project1.AddMetadataReference(MetadataReference.CreateFromFile(typeof(object).Assembly.Location));
project1 = project1.AddAnalyzerReference(new AnalyzerFileReference(typeof(object).Assembly.Location, new TestAnalyzerAssemblyLoader()));
var textDocument1 = project1.AddAdditionalDocument("Additional", SourceText.From("hello"), ImmutableArray.Create("test"), @".\Add");
return textDocument1.Project.Solution;
project1 = project1.AddAdditionalDocument("Additional", SourceText.From("hello"), ImmutableArray.Create("test"), @".\Add").Project;
return project1.Solution.AddAnalyzerConfigDocuments(
ImmutableArray.Create(
DocumentInfo.Create(
DocumentId.CreateNewId(project1.Id),
".editorconfig",
loader: TextLoader.From(TextAndVersion.Create(SourceText.From("root = true"), VersionStamp.Create())))));
}
internal static async Task VerifyAssetAsync(IRemotableDataService service, SolutionStateChecksums solutionObject)
......@@ -97,6 +103,12 @@ internal static async Task VerifyAssetAsync(IRemotableDataService service, Proje
var documentObject = await service.GetValueAsync<DocumentStateChecksums>(checksum).ConfigureAwait(false);
await VerifyAssetAsync(service, documentObject).ConfigureAwait(false);
}
foreach (var checksum in projectObject.AnalyzerConfigDocuments)
{
var documentObject = await service.GetValueAsync<DocumentStateChecksums>(checksum).ConfigureAwait(false);
await VerifyAssetAsync(service, documentObject).ConfigureAwait(false);
}
}
internal static async Task VerifyAssetAsync(IRemotableDataService service, DocumentStateChecksums documentObject)
......@@ -150,6 +162,7 @@ internal static void ProjectStateEqual(IRemotableDataService service, ProjectSta
ChecksumWithChildrenEqual(projectObjects1.Documents.ToDocumentObjects(service), projectObjects2.Documents.ToDocumentObjects(service));
ChecksumWithChildrenEqual(projectObjects1.AdditionalDocuments.ToDocumentObjects(service), projectObjects2.AdditionalDocuments.ToDocumentObjects(service));
ChecksumWithChildrenEqual(projectObjects1.AnalyzerConfigDocuments.ToDocumentObjects(service), projectObjects2.AnalyzerConfigDocuments.ToDocumentObjects(service));
}
internal static void ProjectStatesEqual(IRemotableDataService service, ChecksumObjectCollection<ProjectStateChecksums> projectObjects1, ChecksumObjectCollection<ProjectStateChecksums> projectObjects2)
......
......@@ -69,6 +69,7 @@ private async Task SynchronizeProjectAssets_NoLockAsync(IEnumerable<Checksum> pr
await CollectChecksumChildrenAsync(checksums, projectChecksumObject.Documents, cancellationToken).ConfigureAwait(false);
await CollectChecksumChildrenAsync(checksums, projectChecksumObject.AdditionalDocuments, cancellationToken).ConfigureAwait(false);
await CollectChecksumChildrenAsync(checksums, projectChecksumObject.AnalyzerConfigDocuments, cancellationToken).ConfigureAwait(false);
}
await _assetService.SynchronizeAssetsAsync(checksums, cancellationToken).ConfigureAwait(false);
......
......@@ -15,6 +15,7 @@
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using System.Diagnostics;
using System;
namespace Microsoft.CodeAnalysis.Remote
{
......@@ -221,13 +222,37 @@ private async Task<Solution> UpdateProjectAsync(Project project, ProjectStateChe
// changed analyzer references
if (oldProjectChecksums.Documents.Checksum != newProjectChecksums.Documents.Checksum)
{
project = await UpdateDocumentsAsync(project, oldProjectChecksums.Documents, newProjectChecksums.Documents, additionalText: false).ConfigureAwait(false);
project = await UpdateDocumentsAsync(
project,
project.State.DocumentStates.Values,
oldProjectChecksums.Documents,
newProjectChecksums.Documents,
(solution, documents) => solution.AddDocuments(documents),
(solution, documentId) => solution.RemoveDocument(documentId)).ConfigureAwait(false);
}
// changed additional documents
if (oldProjectChecksums.AdditionalDocuments.Checksum != newProjectChecksums.AdditionalDocuments.Checksum)
{
project = await UpdateDocumentsAsync(project, oldProjectChecksums.AdditionalDocuments, newProjectChecksums.AdditionalDocuments, additionalText: true).ConfigureAwait(false);
project = await UpdateDocumentsAsync(
project,
project.State.AdditionalDocumentStates.Values,
oldProjectChecksums.AdditionalDocuments,
newProjectChecksums.AdditionalDocuments,
(solution, documents) => solution.AddAdditionalDocuments(documents),
(solution, documentId) => solution.RemoveAdditionalDocument(documentId)).ConfigureAwait(false);
}
// changed analyzer config documents
if (oldProjectChecksums.AnalyzerConfigDocuments.Checksum != newProjectChecksums.AnalyzerConfigDocuments.Checksum)
{
project = await UpdateDocumentsAsync(
project,
project.State.AnalyzerConfigDocumentStates.Values,
oldProjectChecksums.AnalyzerConfigDocuments,
newProjectChecksums.AnalyzerConfigDocuments,
(solution, documents) => solution.AddAnalyzerConfigDocuments(documents),
(solution, documentId) => solution.RemoveAnalyzerConfigDocument(documentId)).ConfigureAwait(false);
}
return project.Solution;
......@@ -280,7 +305,13 @@ private async Task<Project> UpdateProjectInfoAsync(Project project, Checksum inf
return project;
}
private async Task<Project> UpdateDocumentsAsync(Project project, ChecksumCollection oldChecksums, ChecksumCollection newChecksums, bool additionalText)
private async Task<Project> UpdateDocumentsAsync(
Project project,
IEnumerable<TextDocumentState> existingTextDocumentStates,
ChecksumCollection oldChecksums,
ChecksumCollection newChecksums,
Func<Solution, ImmutableArray<DocumentInfo>, Solution> addDocuments,
Func<Solution, DocumentId, Solution> removeDocument)
{
using (var olds = SharedPools.Default<HashSet<Checksum>>().GetPooledObject())
using (var news = SharedPools.Default<HashSet<Checksum>>().GetPooledObject())
......@@ -292,19 +323,28 @@ private async Task<Project> UpdateDocumentsAsync(Project project, ChecksumCollec
olds.Object.ExceptWith(newChecksums);
news.Object.ExceptWith(oldChecksums);
var oldMap = await GetDocumentMapAsync(project, olds.Object, additionalText).ConfigureAwait(false);
var oldMap = await GetDocumentMapAsync(project, existingTextDocumentStates, olds.Object).ConfigureAwait(false);
var newMap = await GetDocumentMapAsync(_assetService, news.Object).ConfigureAwait(false);
// added document
ImmutableArray<DocumentInfo>.Builder documentsToAdd = null;
foreach (var kv in newMap)
{
if (!oldMap.ContainsKey(kv.Key))
{
documentsToAdd = documentsToAdd ?? ImmutableArray.CreateBuilder<DocumentInfo>();
// we have new document added
project = AddDocument(project, await CreateDocumentInfoAsync(kv.Value.Checksum).ConfigureAwait(false), additionalText);
var documentInfo = await CreateDocumentInfoAsync(kv.Value.Checksum).ConfigureAwait(false);
documentsToAdd.Add(documentInfo);
}
}
if (documentsToAdd != null)
{
project = addDocuments(project.Solution, documentsToAdd.ToImmutable()).GetProject(project.Id);
}
// changed document
foreach (var kv in newMap)
{
......@@ -316,8 +356,8 @@ private async Task<Project> UpdateDocumentsAsync(Project project, ChecksumCollec
var newDocumentChecksums = kv.Value;
Contract.ThrowIfTrue(oldDocumentChecksums.Checksum == newDocumentChecksums.Checksum);
var document = additionalText ? project.GetAdditionalDocument(kv.Key) : project.GetDocument(kv.Key);
project = await UpdateDocumentAsync(document, oldDocumentChecksums, newDocumentChecksums, additionalText).ConfigureAwait(false);
var document = project.GetDocument(kv.Key) ?? project.GetAdditionalDocument(kv.Key) ?? project.GetAnalyzerConfigDocument(kv.Key);
project = await UpdateDocumentAsync(document, oldDocumentChecksums, newDocumentChecksums).ConfigureAwait(false);
}
// removed document
......@@ -326,14 +366,7 @@ private async Task<Project> UpdateDocumentsAsync(Project project, ChecksumCollec
if (!newMap.ContainsKey(kv.Key))
{
// we have a document removed
if (additionalText)
{
project = project.RemoveAdditionalDocument(kv.Key);
}
else
{
project = project.RemoveDocument(kv.Key);
}
project = removeDocument(project.Solution, kv.Key).GetProject(project.Id);
}
}
......@@ -341,12 +374,12 @@ private async Task<Project> UpdateDocumentsAsync(Project project, ChecksumCollec
}
}
private async Task<Project> UpdateDocumentAsync(TextDocument document, DocumentStateChecksums oldDocumentChecksums, DocumentStateChecksums newDocumentChecksums, bool additionalText)
private async Task<Project> UpdateDocumentAsync(TextDocument document, DocumentStateChecksums oldDocumentChecksums, DocumentStateChecksums newDocumentChecksums)
{
// changed info
if (oldDocumentChecksums.Info != newDocumentChecksums.Info)
{
document = await UpdateDocumentInfoAsync(document, newDocumentChecksums.Info, additionalText).ConfigureAwait(false);
document = await UpdateDocumentInfoAsync(document, newDocumentChecksums.Info).ConfigureAwait(false);
}
// changed text
......@@ -354,20 +387,25 @@ private async Task<Project> UpdateDocumentAsync(TextDocument document, DocumentS
{
var sourceText = await _assetService.GetAssetAsync<SourceText>(newDocumentChecksums.Text, _cancellationToken).ConfigureAwait(false);
if (additionalText)
if (document is Document)
{
document = document.Project.Solution.WithAdditionalDocumentText(document.Id, sourceText).GetAdditionalDocument(document.Id);
document = document.Project.Solution.WithDocumentText(document.Id, sourceText).GetDocument(document.Id);
}
else if (document is AnalyzerConfigDocument)
{
document = document.Project.Solution.WithAnalyzerConfigDocumentText(document.Id, sourceText).GetAnalyzerConfigDocument(document.Id);
}
else
{
document = document.Project.Solution.WithDocumentText(document.Id, sourceText).GetDocument(document.Id);
Debug.Assert(document.Project.ContainsAdditionalDocument(document.Id));
document = document.Project.Solution.WithAdditionalDocumentText(document.Id, sourceText).GetAdditionalDocument(document.Id);
}
}
return document.Project;
}
private async Task<TextDocument> UpdateDocumentInfoAsync(TextDocument document, Checksum infoChecksum, bool additionalText)
private async Task<TextDocument> UpdateDocumentInfoAsync(TextDocument document, Checksum infoChecksum)
{
var newDocumentInfo = await _assetService.GetAssetAsync<DocumentInfo.DocumentAttributes>(infoChecksum, _cancellationToken).ConfigureAwait(false);
......@@ -380,14 +418,14 @@ private async Task<TextDocument> UpdateDocumentInfoAsync(TextDocument document,
if (document.State.Attributes.Folders != newDocumentInfo.Folders)
{
// additional document can't change folder once created
Contract.ThrowIfTrue(additionalText);
Contract.ThrowIfFalse(document is Document);
document = document.Project.Solution.WithDocumentFolders(document.Id, newDocumentInfo.Folders).GetDocument(document.Id);
}
if (document.State.Attributes.SourceCodeKind != newDocumentInfo.SourceCodeKind)
{
// additional document can't change sourcecode kind once created
Contract.ThrowIfTrue(additionalText);
Contract.ThrowIfFalse(document is Document);
document = document.Project.Solution.WithDocumentSourceCodeKind(document.Id, newDocumentInfo.SourceCodeKind).GetDocument(document.Id);
}
......@@ -410,27 +448,16 @@ private async Task<TextDocument> UpdateDocumentInfoAsync(TextDocument document,
return map;
}
private Task<Dictionary<DocumentId, DocumentStateChecksums>> GetDocumentMapAsync(Project project, HashSet<Checksum> documents, bool additionalText)
{
if (additionalText)
{
return GetDocumentMapAsync(project, project.State.AdditionalDocumentStates, documents);
}
return GetDocumentMapAsync(project, project.State.DocumentStates, documents);
}
private async Task<Dictionary<DocumentId, DocumentStateChecksums>> GetDocumentMapAsync<T>(Project project, IImmutableDictionary<DocumentId, T> states, HashSet<Checksum> documents)
where T : TextDocumentState
private async Task<Dictionary<DocumentId, DocumentStateChecksums>> GetDocumentMapAsync(Project project, IEnumerable<TextDocumentState> states, HashSet<Checksum> documents)
{
var map = new Dictionary<DocumentId, DocumentStateChecksums>();
foreach (var kv in states)
foreach (var state in states)
{
var documentChecksums = await kv.Value.GetStateChecksumsAsync(_cancellationToken).ConfigureAwait(false);
var documentChecksums = await state.GetStateChecksumsAsync(_cancellationToken).ConfigureAwait(false);
if (documents.Contains(documentChecksums.Checksum))
{
map.Add(kv.Key, documentChecksums);
map.Add(state.Id, documentChecksums);
}
}
......@@ -496,6 +523,7 @@ private async Task<ProjectInfo> CreateProjectInfoAsync(Checksum projectChecksum)
var documentInfos = await CreateDocumentInfosAsync(projectSnapshot.Documents).ConfigureAwait(false);
var additionalDocumentInfos = await CreateDocumentInfosAsync(projectSnapshot.AdditionalDocuments).ConfigureAwait(false);
var analyzerConfigDocumentInfos = await CreateDocumentInfosAsync(projectSnapshot.AnalyzerConfigDocuments).ConfigureAwait(false);
return ProjectInfo.Create(
projectInfo.Id, projectInfo.Version, projectInfo.Name, projectInfo.AssemblyName,
......@@ -504,7 +532,8 @@ private async Task<ProjectInfo> CreateProjectInfoAsync(Checksum projectChecksum)
documentInfos, p2p, metadata, analyzers, additionalDocumentInfos, projectInfo.IsSubmission)
.WithOutputRefFilePath(projectInfo.OutputRefFilePath)
.WithHasAllInformation(projectInfo.HasAllInformation)
.WithDefaultNamespace(projectInfo.DefaultNamespace);
.WithDefaultNamespace(projectInfo.DefaultNamespace)
.WithAnalyzerConfigDocuments(analyzerConfigDocumentInfos);
}
private async Task<List<T>> CreateCollectionAsync<T>(ChecksumCollection collections)
......
......@@ -58,7 +58,7 @@ private static async Task AppendAssetMapAsync(this Project project, Dictionary<C
projectChecksums.Find(project.State, Flatten(projectChecksums), map, cancellationToken);
foreach (var document in project.Documents.Concat(project.AdditionalDocuments))
foreach (var document in project.Documents.Concat(project.AdditionalDocuments).Concat(project.AnalyzerConfigDocuments))
{
await document.AppendAssetMapAsync(map, cancellationToken).ConfigureAwait(false);
}
......
......@@ -113,7 +113,7 @@ async Task<HashSet<Checksum>> GetAllChildrenChecksumsAsync(Checksum solutionChec
var projectChecksums = await assetService.GetAssetAsync<ProjectStateChecksums>(projectChecksum, CancellationToken.None).ConfigureAwait(false);
set.AppendChecksums(projectChecksums);
foreach (var documentChecksum in projectChecksums.Documents.Concat(projectChecksums.AdditionalDocuments))
foreach (var documentChecksum in projectChecksums.Documents.Concat(projectChecksums.AdditionalDocuments).Concat(projectChecksums.AnalyzerConfigDocuments))
{
var documentChecksums = await assetService.GetAssetAsync<DocumentStateChecksums>(documentChecksum, CancellationToken.None).ConfigureAwait(false);
set.AppendChecksums(documentChecksums);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册