提交 078e8637 编写于 作者: J Jason Malinowski

Add Workspace APIs to add and remove AnalyzerConfigDocuments

This change is a fairly mechanical change and is just flowing through
the new APIs that create new documents; all the "interesting" stuff
of the actual implementation will follow.
上级 95977528
......@@ -25,6 +25,7 @@ public static WellKnownSynchronizationKind GetWellKnownSynchronizationKind(this
case ProjectChecksumCollection _: return WellKnownSynchronizationKind.Projects;
case DocumentChecksumCollection _: return WellKnownSynchronizationKind.Documents;
case TextDocumentChecksumCollection _: return WellKnownSynchronizationKind.TextDocuments;
case AnalyzerConfigDocumentChecksumCollection _: return WellKnownSynchronizationKind.AnalyzerConfigDocuments;
case ProjectReferenceChecksumCollection _: return WellKnownSynchronizationKind.ProjectReferences;
case MetadataReferenceChecksumCollection _: return WellKnownSynchronizationKind.MetadataReferences;
case AnalyzerReferenceChecksumCollection _: return WellKnownSynchronizationKind.AnalyzerReferences;
......
......@@ -160,6 +160,7 @@ public T Deserialize<T>(WellKnownSynchronizationKind kind, ObjectReader reader,
case WellKnownSynchronizationKind.Projects:
case WellKnownSynchronizationKind.Documents:
case WellKnownSynchronizationKind.TextDocuments:
case WellKnownSynchronizationKind.AnalyzerConfigDocuments:
case WellKnownSynchronizationKind.ProjectReferences:
case WellKnownSynchronizationKind.MetadataReferences:
case WellKnownSynchronizationKind.AnalyzerReferences:
......
......@@ -89,6 +89,7 @@ private ChecksumWithChildren DeserializeChecksumWithChildren(ObjectReader reader
.Add(WellKnownSynchronizationKind.Projects, children => new ProjectChecksumCollection(children))
.Add(WellKnownSynchronizationKind.Documents, children => new DocumentChecksumCollection(children))
.Add(WellKnownSynchronizationKind.TextDocuments, children => new TextDocumentChecksumCollection(children))
.Add(WellKnownSynchronizationKind.AnalyzerConfigDocuments, children => new AnalyzerConfigDocumentChecksumCollection(children))
.Add(WellKnownSynchronizationKind.ProjectReferences, children => new ProjectReferenceChecksumCollection(children))
.Add(WellKnownSynchronizationKind.MetadataReferences, children => new MetadataReferenceChecksumCollection(children))
.Add(WellKnownSynchronizationKind.AnalyzerReferences, children => new AnalyzerReferenceChecksumCollection(children));
......
......@@ -15,6 +15,7 @@ internal enum WellKnownSynchronizationKind
Projects,
Documents,
TextDocuments,
AnalyzerConfigDocuments,
ProjectReferences,
MetadataReferences,
AnalyzerReferences,
......@@ -43,6 +44,7 @@ internal enum WellKnownSynchronizationKind
AnalyzerReferenceChecksumCollection,
TextDocumentChecksumCollection,
DocumentChecksumCollection,
AnalyzerConfigDocumentChecksumCollection,
ProjectChecksumCollection,
SolutionStateChecksums,
ProjectStateChecksums,
......
*REMOVED*Microsoft.CodeAnalysis.Workspace.ClearOpenDocument(Microsoft.CodeAnalysis.DocumentId documentId, bool isSolutionClosing = false) -> void
Microsoft.CodeAnalysis.Solution.AddAnalyzerConfigDocuments(System.Collections.Immutable.ImmutableArray<Microsoft.CodeAnalysis.DocumentInfo> documentInfos) -> Microsoft.CodeAnalysis.Solution
Microsoft.CodeAnalysis.Solution.ContainsAnalyzerConfigDocument(Microsoft.CodeAnalysis.DocumentId documentId) -> bool
Microsoft.CodeAnalysis.Solution.RemoveAnalyzerConfigDocument(Microsoft.CodeAnalysis.DocumentId documentId) -> Microsoft.CodeAnalysis.Solution
Microsoft.CodeAnalysis.Workspace.CheckAnalyzerConfigDocumentIsInCurrentSolution(Microsoft.CodeAnalysis.DocumentId documentId) -> void
Microsoft.CodeAnalysis.Workspace.CheckAnalyzerConfigDocumentIsNotInCurrentSolution(Microsoft.CodeAnalysis.DocumentId documentId) -> void
Microsoft.CodeAnalysis.Workspace.OnAnalyzerConfigDocumentAdded(Microsoft.CodeAnalysis.DocumentInfo documentInfo) -> void
Microsoft.CodeAnalysis.Workspace.OnAnalyzerConfigDocumentRemoved(Microsoft.CodeAnalysis.DocumentId documentId) -> void
Microsoft.CodeAnalysis.WorkspaceChangeKind.AnalyzerConfigDocumentAdded = 18 -> Microsoft.CodeAnalysis.WorkspaceChangeKind
Microsoft.CodeAnalysis.WorkspaceChangeKind.AnalyzerConfigDocumentChanged = 21 -> Microsoft.CodeAnalysis.WorkspaceChangeKind
Microsoft.CodeAnalysis.WorkspaceChangeKind.AnalyzerConfigDocumentReloaded = 20 -> Microsoft.CodeAnalysis.WorkspaceChangeKind
Microsoft.CodeAnalysis.WorkspaceChangeKind.AnalyzerConfigDocumentRemoved = 19 -> Microsoft.CodeAnalysis.WorkspaceChangeKind
abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.AliasImportDeclaration(string aliasIdentifierName, Microsoft.CodeAnalysis.SyntaxNode name) -> Microsoft.CodeAnalysis.SyntaxNode
abstract Microsoft.CodeAnalysis.Editing.SyntaxGenerator.NameExpression(Microsoft.CodeAnalysis.INamespaceOrTypeSymbol namespaceOrTypeSymbol) -> Microsoft.CodeAnalysis.SyntaxNode
const Microsoft.CodeAnalysis.Classification.ClassificationTypeNames.ControlKeyword = "keyword - control" -> string
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis
{
internal sealed class AnalyzerConfigDocumentState : TextDocumentState
{
private AnalyzerConfigDocumentState(
SolutionServices solutionServices,
IDocumentServiceProvider documentServiceProvider,
DocumentInfo.DocumentAttributes attributes,
SourceText sourceTextOpt,
ValueSource<TextAndVersion> textAndVersionSource)
: base(solutionServices, documentServiceProvider, attributes, sourceTextOpt, textAndVersionSource)
{
}
public AnalyzerConfigDocumentState(
DocumentInfo documentInfo,
SolutionServices solutionServices)
: base(documentInfo, solutionServices)
{
}
}
}
......@@ -53,6 +53,12 @@ internal class TextDocumentChecksumCollection : ChecksumCollection
public TextDocumentChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKind.TextDocumentChecksumCollection, checksums) { }
}
internal class AnalyzerConfigDocumentChecksumCollection : ChecksumCollection
{
public AnalyzerConfigDocumentChecksumCollection(Checksum[] checksums) : this((object[])checksums) { }
public AnalyzerConfigDocumentChecksumCollection(object[] checksums) : base(WellKnownSynchronizationKind.AnalyzerConfigDocumentChecksumCollection, checksums) { }
}
internal class ProjectReferenceChecksumCollection : ChecksumCollection
{
public ProjectReferenceChecksumCollection(Checksum[] checksums) : this((object[])checksums) { }
......
......@@ -33,6 +33,12 @@ internal partial class ProjectState
/// </summary>
private readonly ImmutableSortedDictionary<DocumentId, TextDocumentState> _additionalDocumentStates;
/// <summary>
/// The analyzer config documents in this project. They are sorted by <see cref="DocumentId.Id"/> to provide a stable sort for
/// <see cref="GetChecksumAsync(CancellationToken)"/>.
/// </summary>
private readonly ImmutableSortedDictionary<DocumentId, AnalyzerConfigDocumentState> _analyzerConfigDocumentStates;
private readonly ImmutableList<DocumentId> _documentIds;
private readonly ImmutableList<DocumentId> _additionalDocumentIds;
private readonly AsyncLazy<VersionStamp> _lazyLatestDocumentVersion;
......@@ -52,6 +58,7 @@ internal partial class ProjectState
ImmutableList<DocumentId> additionalDocumentIds,
ImmutableSortedDictionary<DocumentId, DocumentState> documentStates,
ImmutableSortedDictionary<DocumentId, TextDocumentState> additionalDocumentStates,
ImmutableSortedDictionary<DocumentId, AnalyzerConfigDocumentState> analyzerConfigDocumentStates,
AsyncLazy<VersionStamp> lazyLatestDocumentVersion,
AsyncLazy<VersionStamp> lazyLatestDocumentTopLevelChangeVersion)
{
......@@ -61,6 +68,7 @@ internal partial class ProjectState
_additionalDocumentIds = additionalDocumentIds;
_documentStates = documentStates;
_additionalDocumentStates = additionalDocumentStates;
_analyzerConfigDocumentStates = analyzerConfigDocumentStates;
_lazyLatestDocumentVersion = lazyLatestDocumentVersion;
_lazyLatestDocumentTopLevelChangeVersion = lazyLatestDocumentTopLevelChangeVersion;
......@@ -344,6 +352,11 @@ public bool ContainsAdditionalDocument(DocumentId documentId)
return _additionalDocumentStates.ContainsKey(documentId);
}
public bool ContainsAnalyzerConfigDocument(DocumentId documentId)
{
return _analyzerConfigDocumentStates.ContainsKey(documentId);
}
public DocumentState GetDocumentState(DocumentId documentId)
{
_documentStates.TryGetValue(documentId, out var state);
......@@ -356,12 +369,19 @@ public TextDocumentState GetAdditionalDocumentState(DocumentId documentId)
return state;
}
public AnalyzerConfigDocumentState GetAnalyzerConfigDocumentState(DocumentId documentId)
{
_analyzerConfigDocumentStates.TryGetValue(documentId, out var state);
return state;
}
private ProjectState With(
ProjectInfo projectInfo = null,
ImmutableList<DocumentId> documentIds = default,
ImmutableList<DocumentId> additionalDocumentIds = default,
ImmutableSortedDictionary<DocumentId, DocumentState> documentStates = null,
ImmutableSortedDictionary<DocumentId, TextDocumentState> additionalDocumentStates = null,
ImmutableSortedDictionary<DocumentId, AnalyzerConfigDocumentState> analyzerConfigDocumentStates = null,
AsyncLazy<VersionStamp> latestDocumentVersion = null,
AsyncLazy<VersionStamp> latestDocumentTopLevelChangeVersion = null)
{
......@@ -373,6 +393,7 @@ public TextDocumentState GetAdditionalDocumentState(DocumentId documentId)
additionalDocumentIds ?? _additionalDocumentIds,
documentStates ?? _documentStates,
additionalDocumentStates ?? _additionalDocumentStates,
analyzerConfigDocumentStates ?? _analyzerConfigDocumentStates,
latestDocumentVersion ?? _lazyLatestDocumentVersion,
latestDocumentTopLevelChangeVersion ?? _lazyLatestDocumentTopLevelChangeVersion);
}
......@@ -601,6 +622,15 @@ public ProjectState AddAdditionalDocument(TextDocumentState document)
additionalDocumentStates: _additionalDocumentStates.Add(document.Id, document));
}
public ProjectState AddAnalyzerConfigDocuments(ImmutableArray<AnalyzerConfigDocumentState> documents)
{
Debug.Assert(!documents.Any(d => this._analyzerConfigDocumentStates.ContainsKey(d.Id)));
return this.With(
projectInfo: this.ProjectInfo.WithVersion(this.Version.GetNewerVersion()),
analyzerConfigDocumentStates: _analyzerConfigDocumentStates.AddRange(documents.Select(d => KeyValuePairUtil.Create(d.Id, d))));
}
public ProjectState RemoveDocument(DocumentId documentId)
{
Debug.Assert(this.DocumentStates.ContainsKey(documentId));
......@@ -621,6 +651,15 @@ public ProjectState RemoveAdditionalDocument(DocumentId documentId)
additionalDocumentStates: _additionalDocumentStates.Remove(documentId));
}
public ProjectState RemoveAnalyzerConfigDocument(DocumentId documentId)
{
Debug.Assert(this._analyzerConfigDocumentStates.ContainsKey(documentId));
return this.With(
projectInfo: this.ProjectInfo.WithVersion(this.Version.GetNewerVersion()),
analyzerConfigDocumentStates: _analyzerConfigDocumentStates.Remove(documentId));
}
public ProjectState RemoveAllDocuments()
{
return this.With(
......
......@@ -35,6 +35,7 @@ private async Task<ProjectStateChecksums> ComputeChecksumsAsync(CancellationToke
// sorted by ID so we have a consistent sort.
var documentChecksumsTasks = _documentStates.Select(pair => pair.Value.GetChecksumAsync(cancellationToken));
var additionalDocumentChecksumTasks = _additionalDocumentStates.Select(pair => pair.Value.GetChecksumAsync(cancellationToken));
var analyzerConfigDocumentChecksumTasks = _analyzerConfigDocumentStates.Select(pair => pair.Value.GetChecksumAsync(cancellationToken));
var serializer = _solutionServices.Workspace.Services.GetService<ISerializerService>();
......@@ -50,6 +51,7 @@ private async Task<ProjectStateChecksums> ComputeChecksumsAsync(CancellationToke
var documentChecksums = await Task.WhenAll(documentChecksumsTasks).ConfigureAwait(false);
var additionalChecksums = await Task.WhenAll(additionalDocumentChecksumTasks).ConfigureAwait(false);
var analyzerConfigDocumentChecksums = await Task.WhenAll(analyzerConfigDocumentChecksumTasks).ConfigureAwait(false);
return new ProjectStateChecksums(
infoChecksum,
......@@ -59,7 +61,8 @@ private async Task<ProjectStateChecksums> ComputeChecksumsAsync(CancellationToke
projectReferenceChecksums,
metadataReferenceChecksums,
analyzerReferenceChecksums,
new TextDocumentChecksumCollection(additionalChecksums));
new TextDocumentChecksumCollection(additionalChecksums),
new AnalyzerConfigDocumentChecksumCollection(analyzerConfigDocumentChecksums));
}
}
}
......
......@@ -129,6 +129,11 @@ public Project GetProject(IAssemblySymbol assemblySymbol, CancellationToken canc
/// </summary>
public bool ContainsAdditionalDocument(DocumentId documentId) => _state.ContainsAdditionalDocument(documentId);
/// <summary>
/// True if the solution contains the analyzer config document in one of its projects
/// </summary>
public bool ContainsAnalyzerConfigDocument(DocumentId documentId) => _state.ContainsAnalyzerConfigDocument(documentId);
/// <summary>
/// Gets the documentId in this solution with the specified syntax tree.
/// </summary>
......@@ -780,6 +785,20 @@ public Solution AddAdditionalDocument(DocumentInfo documentInfo)
return new Solution(newState);
}
/// <summary>
/// Creates a new Solution instance that contains a new compiler configuration document like a .editorconfig file.
/// </summary>
public Solution AddAnalyzerConfigDocuments(ImmutableArray<DocumentInfo> documentInfos)
{
var newState = _state.AddAnalyzerConfigDocuments(documentInfos);
if (newState == _state)
{
return this;
}
return new Solution(newState);
}
/// <summary>
/// Creates a new solution instance that no longer includes the specified document.
/// </summary>
......@@ -808,6 +827,17 @@ public Solution RemoveAdditionalDocument(DocumentId documentId)
return new Solution(newState);
}
public Solution RemoveAnalyzerConfigDocument(DocumentId documentId)
{
var newState = _state.RemoveAnalyzerConfigDocument(documentId);
if (newState == _state)
{
return this;
}
return new Solution(newState);
}
/// <summary>
/// Creates a new solution instance with the document specified updated to have the new name.
/// </summary>
......
......@@ -291,6 +291,17 @@ public bool ContainsAdditionalDocument(DocumentId documentId)
this.GetProjectState(documentId.ProjectId).ContainsAdditionalDocument(documentId);
}
/// <summary>
/// True if the solution contains the analyzer config document in one of its projects
/// </summary>
public bool ContainsAnalyzerConfigDocument(DocumentId documentId)
{
return
documentId != null &&
this.ContainsProject(documentId.ProjectId) &&
this.GetProjectState(documentId.ProjectId).ContainsAnalyzerConfigDocument(documentId);
}
private DocumentState GetDocumentState(DocumentId documentId)
{
if (documentId != null)
......@@ -1198,6 +1209,29 @@ public SolutionState AddAdditionalDocument(DocumentInfo documentInfo)
newFilePathToDocumentIdsMap: CreateFilePathToDocumentIdsMapWithAddedDocuments(documentStates));
}
public SolutionState AddAnalyzerConfigDocuments(ImmutableArray<DocumentInfo> documentInfos)
{
// Adding a new analyzer config potentially impacts all syntax trees and the diagnostic reporting information
// attached to them, so we'll just replace all syntax trees in that case.
return AddDocumentsToMultipleProjects(documentInfos,
(documentInfo, project) => new AnalyzerConfigDocumentState(documentInfo, _solutionServices),
(projectState, documents) => (projectState.AddAnalyzerConfigDocuments(documents), new CompilationTranslationAction.ReplaceAllSyntaxTreesAction(projectState)));
}
public SolutionState RemoveAnalyzerConfigDocument(DocumentId documentId)
{
CheckContainsAnalyzerConfigDocument(documentId);
var oldProject = this.GetProjectState(documentId.ProjectId);
var newProject = oldProject.RemoveAnalyzerConfigDocument(documentId);
var documentStates = SpecializedCollections.SingletonEnumerable(oldProject.GetAnalyzerConfigDocumentState(documentId));
return this.ForkProject(
newProject,
new CompilationTranslationAction.ReplaceAllSyntaxTreesAction(newProject),
newFilePathToDocumentIdsMap: CreateFilePathToDocumentIdsMapWithRemovedDocuments(documentStates));
}
/// <summary>
/// Creates a new solution instance that no longer includes the specified document.
/// </summary>
......@@ -2049,5 +2083,15 @@ private void CheckContainsAdditionalDocument(DocumentId documentId)
throw new InvalidOperationException(WorkspacesResources.The_solution_does_not_contain_the_specified_document);
}
}
private void CheckContainsAnalyzerConfigDocument(DocumentId documentId)
{
Debug.Assert(this.ContainsAnalyzerConfigDocument(documentId));
if (!this.ContainsAnalyzerConfigDocument(documentId))
{
throw new InvalidOperationException(WorkspacesResources.The_solution_does_not_contain_the_specified_document);
}
}
}
}
......@@ -81,7 +81,8 @@ internal class ProjectStateChecksums : ChecksumWithChildren
ProjectReferenceChecksumCollection projectReferenceChecksums,
MetadataReferenceChecksumCollection metadataReferenceChecksums,
AnalyzerReferenceChecksumCollection analyzerReferenceChecksums,
TextDocumentChecksumCollection additionalDocumentChecksums) :
TextDocumentChecksumCollection additionalDocumentChecksums,
AnalyzerConfigDocumentChecksumCollection analyzerConfigDocumentChecksumCollection) :
this(
(object)infoChecksum,
compilationOptionsChecksum,
......@@ -90,7 +91,8 @@ internal class ProjectStateChecksums : ChecksumWithChildren
projectReferenceChecksums,
metadataReferenceChecksums,
analyzerReferenceChecksums,
additionalDocumentChecksums)
additionalDocumentChecksums,
analyzerConfigDocumentChecksumCollection)
{
}
......
......@@ -902,6 +902,46 @@ protected internal void OnAdditionalDocumentRemoved(DocumentId documentId)
}
}
/// <summary>
/// Call this method when an analyzer config document is added to a project in the host environment.
/// </summary>
protected internal void OnAnalyzerConfigDocumentAdded(DocumentInfo documentInfo)
{
using (_serializationLock.DisposableWait())
{
var documentId = documentInfo.Id;
CheckProjectIsInCurrentSolution(documentId.ProjectId);
CheckAnalyzerConfigDocumentIsNotInCurrentSolution(documentId);
var oldSolution = this.CurrentSolution;
var newSolution = this.SetCurrentSolution(oldSolution.AddAnalyzerConfigDocuments(ImmutableArray.Create(documentInfo)));
this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.AnalyzerConfigDocumentAdded, oldSolution, newSolution, documentId: documentId);
}
}
/// <summary>
/// Call this method when an analyzer config document is removed from a project in the host environment.
/// </summary>
protected internal void OnAnalyzerConfigDocumentRemoved(DocumentId documentId)
{
using (_serializationLock.DisposableWait())
{
CheckAnalyzerConfigDocumentIsInCurrentSolution(documentId);
this.CheckDocumentCanBeRemoved(documentId);
var oldSolution = this.CurrentSolution;
this.ClearDocumentData(documentId);
var newSolution = this.SetCurrentSolution(oldSolution.RemoveAdditionalDocument(documentId));
this.RaiseWorkspaceChangedEventAsync(WorkspaceChangeKind.AnalyzerConfigDocumentRemoved, oldSolution, newSolution, documentId: documentId);
}
}
/// <summary>
/// Updates all projects to properly reference other projects as project references instead of metadata references.
/// </summary>
......@@ -1725,6 +1765,19 @@ protected void CheckAdditionalDocumentIsInCurrentSolution(DocumentId documentId)
}
}
/// <summary>
/// Throws an exception if an analyzer config is not part of the current solution.
/// </summary>
protected void CheckAnalyzerConfigDocumentIsInCurrentSolution(DocumentId documentId)
{
if (!this.CurrentSolution.ContainsAnalyzerConfigDocument(documentId))
{
throw new ArgumentException(string.Format(
WorkspacesResources._0_is_not_part_of_the_workspace,
this.GetDocumentName(documentId)));
}
}
/// <summary>
/// Throws an exception if a document is already part of the current solution.
/// </summary>
......@@ -1751,6 +1804,19 @@ protected void CheckAdditionalDocumentIsNotInCurrentSolution(DocumentId document
}
}
/// <summary>
/// Throws an exception if the analyzer config document is already part of the current solution.
/// </summary>
protected void CheckAnalyzerConfigDocumentIsNotInCurrentSolution(DocumentId documentId)
{
if (this.CurrentSolution.ContainsAnalyzerConfigDocument(documentId))
{
throw new ArgumentException(string.Format(
WorkspacesResources._0_is_already_part_of_the_workspace,
this.GetDocumentName(documentId)));
}
}
/// <summary>
/// Gets the name to use for a project in an error message.
/// </summary>
......
......@@ -102,5 +102,26 @@ public enum WorkspaceChangeKind
/// The document in the current solution had is info changed; name, folders, filepath
/// </summary>
DocumentInfoChanged = 17,
/// <summary>
/// An analyzer config document was added to the current solution.
/// </summary>
AnalyzerConfigDocumentAdded = 18,
/// <summary>
/// An analyzer config document was removed from the current solution.
/// </summary>
AnalyzerConfigDocumentRemoved = 19,
/// <summary>
/// An analyzer config document in the current solution was reloaded.
/// </summary>
AnalyzerConfigDocumentReloaded = 20,
/// <summary>
/// An analyzer config document in the current solution was changed.
/// </summary>
AnalyzerConfigDocumentChanged = 21,
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册