未验证 提交 d5e82977 编写于 作者: S Sam Harwell 提交者: GitHub

Merge pull request #30876 from sharwell/document-operations

Document operations
......@@ -5,8 +5,8 @@
using System.Collections.Immutable;
using System.Runtime.CompilerServices;
using System.Threading;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Diagnostics.Telemetry;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Workspaces.Diagnostics;
using Roslyn.Utilities;
......@@ -77,7 +77,7 @@ internal static class DiagnosticResultSerializer
var others = diagnosticDataSerializer.ReadFrom(reader, project, cancellationToken);
var analysisResult = DiagnosticAnalysisResult.CreateFromSerialization(
project.Id,
project,
version,
syntaxLocalMap,
semanticLocalMap,
......@@ -143,7 +143,16 @@ internal static class DiagnosticResultSerializer
for (var i = 0; i < count; i++)
{
var documentId = DocumentId.ReadFrom(reader);
var diagnostics = serializer.ReadFrom(reader, project.GetDocument(documentId), cancellationToken);
var document = project.GetDocument(documentId);
var diagnostics = serializer.ReadFrom(reader, document, cancellationToken);
if (document?.SupportsDiagnostics() == false)
{
// drop diagnostics for document that doesn't support
// diagnostics
continue;
}
map.Add(documentId, GetOrDefault(diagnostics));
}
......
......@@ -7,6 +7,7 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Experimentation;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Shared.Options;
using Microsoft.CodeAnalysis.Workspaces.Diagnostics;
using Roslyn.Utilities;
......@@ -82,7 +83,7 @@ public async Task<DiagnosticAnalysisResult> GetAnalysisDataAsync(Project project
// loading data can be cancelled any time.
var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, lastResult.Version);
var builder = new Builder(project.Id, lastResult.Version, lastResult.DocumentIds);
var builder = new Builder(project, lastResult.Version, lastResult.DocumentIds);
foreach (var documentId in lastResult.DocumentIds)
{
......@@ -141,7 +142,7 @@ public async Task<DiagnosticAnalysisResult> GetAnalysisDataAsync(Document docume
// loading data can be cancelled any time.
var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, lastResult.Version);
var builder = new Builder(document.Project.Id, lastResult.Version);
var builder = new Builder(document.Project, lastResult.Version);
if (!await TryDeserializeDocumentAsync(serializer, document, builder, cancellationToken).ConfigureAwait(false))
{
......@@ -182,7 +183,7 @@ public async Task<DiagnosticAnalysisResult> GetProjectAnalysisDataAsync(Project
// loading data can be cancelled any time.
var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, lastResult.Version);
var builder = new Builder(project.Id, lastResult.Version);
var builder = new Builder(project, lastResult.Version);
if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, s_addOthers, builder, cancellationToken).ConfigureAwait(false))
{
......@@ -300,7 +301,7 @@ private async Task<DiagnosticAnalysisResult> LoadInitialAnalysisDataAsync(Projec
// loading data can be cancelled any time.
var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false);
var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, version);
var builder = new Builder(project.Id, version);
var builder = new Builder(project, version);
foreach (var document in project.Documents)
{
......@@ -327,7 +328,7 @@ private async Task<DiagnosticAnalysisResult> LoadInitialAnalysisDataAsync(Docume
var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false);
var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, version);
var builder = new Builder(project.Id, version);
var builder = new Builder(project, version);
if (!await TryDeserializeDocumentAsync(serializer, document, builder, cancellationToken).ConfigureAwait(false))
{
......@@ -342,7 +343,7 @@ private async Task<DiagnosticAnalysisResult> LoadInitialProjectAnalysisDataAsync
// loading data can be cancelled any time.
var version = await GetDiagnosticVersionAsync(project, cancellationToken).ConfigureAwait(false);
var serializer = new DiagnosticDataSerializer(_owner.AnalyzerVersion, version);
var builder = new Builder(project.Id, version);
var builder = new Builder(project, version);
if (!await TryDeserializeAsync(serializer, project, project.Id, _owner.NonLocalStateName, s_addOthers, builder, cancellationToken).ConfigureAwait(false))
{
......@@ -458,7 +459,7 @@ private bool IsEmpty(DiagnosticAnalysisResult result, DocumentId documentId)
// we have this builder to avoid allocating collections unnecessarily.
private class Builder
{
private readonly ProjectId _projectId;
private readonly Project _project;
private readonly VersionStamp _version;
private readonly ImmutableHashSet<DocumentId> _documentIds;
......@@ -467,9 +468,9 @@ private class Builder
private ImmutableDictionary<DocumentId, ImmutableArray<DiagnosticData>>.Builder _nonLocals;
private ImmutableArray<DiagnosticData> _others;
public Builder(ProjectId projectId, VersionStamp version, ImmutableHashSet<DocumentId> documentIds = null)
public Builder(Project project, VersionStamp version, ImmutableHashSet<DocumentId> documentIds = null)
{
_projectId = projectId;
_project = project;
_version = version;
_documentIds = documentIds;
}
......@@ -496,13 +497,18 @@ public void AddOthers(ProjectId unused, ImmutableArray<DiagnosticData> diagnosti
private void Add(ref ImmutableDictionary<DocumentId, ImmutableArray<DiagnosticData>>.Builder locals, DocumentId documentId, ImmutableArray<DiagnosticData> diagnostics)
{
if (_project.GetDocument(documentId)?.SupportsDiagnostics() == false)
{
return;
}
locals = locals ?? ImmutableDictionary.CreateBuilder<DocumentId, ImmutableArray<DiagnosticData>>();
locals.Add(documentId, diagnostics);
}
public DiagnosticAnalysisResult ToResult()
{
return DiagnosticAnalysisResult.CreateFromSerialization(_projectId, _version,
return DiagnosticAnalysisResult.CreateFromSerialization(_project, _version,
_syntaxLocals?.ToImmutable() ?? ImmutableDictionary<DocumentId, ImmutableArray<DiagnosticData>>.Empty,
_semanticLocals?.ToImmutable() ?? ImmutableDictionary<DocumentId, ImmutableArray<DiagnosticData>>.Empty,
_nonLocals?.ToImmutable() ?? ImmutableDictionary<DocumentId, ImmutableArray<DiagnosticData>>.Empty,
......
......@@ -8,6 +8,7 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Diagnostics.Telemetry;
using Microsoft.CodeAnalysis.ErrorReporting;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Shared.Extensions;
......@@ -227,7 +228,7 @@ private static bool AnalysisEnabled(Document document)
{
// change it to check active file (or visible files), not open files if active file tracking is enabled.
// otherwise, use open file.
return document.IsOpen();
return document.IsOpen() && document.SupportsDiagnostics();
}
private IEnumerable<StateSet> GetStateSetsForFullSolutionAnalysis(IEnumerable<StateSet> stateSets, Project project)
......
......@@ -233,7 +233,9 @@ internal IVisualStudioHostDocument GetHostDocument(DocumentId documentId)
throw new InvalidOperationException(ServicesVSResources.VisualStudioWorkspace_TryApplyChanges_cannot_be_called_from_a_background_thread);
}
var projectChanges = newSolution.GetChanges(this.CurrentSolution).GetProjectChanges().ToList();
var currentSolution = this.CurrentSolution;
var projectChanges = newSolution.GetChanges(currentSolution).GetProjectChanges().ToList();
var projectsToLoad = new HashSet<Guid>();
foreach (var pc in projectChanges)
{
......@@ -267,13 +269,25 @@ internal IVisualStudioHostDocument GetHostDocument(DocumentId documentId)
}
// first make sure we can edit the document we will be updating (check them out from source control, etc)
var changedDocs = projectChanges.SelectMany(pd => pd.GetChangedDocuments(true).Concat(pd.GetChangedAdditionalDocuments())).ToList();
var changedDocs = projectChanges.SelectMany(pd => pd.GetChangedDocuments(true).Concat(pd.GetChangedAdditionalDocuments())).Where(CanApplyChange).ToList();
if (changedDocs.Count > 0)
{
this.EnsureEditableDocuments(changedDocs);
}
return base.TryApplyChanges(newSolution, progressTracker);
bool CanApplyChange(DocumentId documentId)
{
var document = newSolution.GetDocument(documentId) ?? currentSolution.GetDocument(documentId);
if (document == null)
{
// we can have null if documentId is for additional files
return true;
}
return document.CanApplyChange();
}
}
public override bool CanOpenDocuments
......@@ -1149,6 +1163,8 @@ public void EnsureEditableDocuments(IEnumerable<DocumentId> documents)
{
var queryEdit = (IVsQueryEditQuerySave2)ServiceProvider.GlobalProvider.GetService(typeof(SVsQueryEditQuerySave));
// make sure given document id actually exist in current solution and the file is marked as supporting modifications
// and actually has non null file path
var fileNames = documents.Select(GetFilePath).ToArray();
// TODO: meditate about the flags we can pass to this and decide what is most appropriate for Roslyn
......
// 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 System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Host;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Workspaces.Diagnostics
......@@ -100,8 +102,9 @@ public static DiagnosticAnalysisResult CreateFromBuild(Project project, Immutabl
// so we put everything in as semantic local with default version. this lets us to replace those to live diagnostics when needed easily.
var version = VersionStamp.Default;
// filter out any document that doesn't support diagnostics
var group = diagnostics.GroupBy(d => d.DocumentId);
// filter out any document that doesn't support diagnostics.
// g.Key == null means diagnostics on the project which assigned to "others" error category
var group = diagnostics.GroupBy(d => d.DocumentId).Where(g => g.Key == null || project.GetDocument(g.Key).SupportsDiagnostics()).ToList();
var result = new DiagnosticAnalysisResult(
project.Id,
......@@ -117,7 +120,7 @@ public static DiagnosticAnalysisResult CreateFromBuild(Project project, Immutabl
}
public static DiagnosticAnalysisResult CreateFromSerialization(
ProjectId projectId,
Project project,
VersionStamp version,
ImmutableDictionary<DocumentId, ImmutableArray<DiagnosticData>> syntaxLocalMap,
ImmutableDictionary<DocumentId, ImmutableArray<DiagnosticData>> semanticLocalMap,
......@@ -125,8 +128,12 @@ public static DiagnosticAnalysisResult CreateFromBuild(Project project, Immutabl
ImmutableArray<DiagnosticData> others,
ImmutableHashSet<DocumentId> documentIds = null)
{
VerifyDocumentMap(project, syntaxLocalMap);
VerifyDocumentMap(project, semanticLocalMap);
VerifyDocumentMap(project, nonLocalMap);
return new DiagnosticAnalysisResult(
projectId,
project.Id,
version,
syntaxLocalMap,
semanticLocalMap,
......@@ -139,7 +146,7 @@ public static DiagnosticAnalysisResult CreateFromBuild(Project project, Immutabl
public static DiagnosticAnalysisResult CreateFromBuilder(DiagnosticAnalysisResultBuilder builder)
{
return CreateFromSerialization(
builder.Project.Id,
builder.Project,
builder.Version,
builder.SyntaxLocals,
builder.SemanticLocals,
......@@ -233,5 +240,14 @@ private ImmutableHashSet<DocumentId> CreateDocumentIds()
return ImmutableHashSet.CreateRange(documents);
}
[Conditional("DEBUG")]
private static void VerifyDocumentMap(Project project, ImmutableDictionary<DocumentId, ImmutableArray<DiagnosticData>> map)
{
foreach (var documentId in map.Keys)
{
Debug.Assert(project.GetDocument(documentId)?.SupportsDiagnostics() == true);
}
}
}
}
......@@ -5,6 +5,7 @@
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Host;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Workspaces.Diagnostics
......@@ -112,7 +113,7 @@ public void AddExternalSemanticDiagnostics(DocumentId documentId, IEnumerable<Di
private void AppendDiagnostics(ref Dictionary<DocumentId, List<DiagnosticData>> map, Document documentOpt, Diagnostic diagnostic)
{
if (documentOpt is null)
if (documentOpt?.SupportsDiagnostics() == false)
{
return;
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Host
{
internal static class Extensions
{
public static bool CanApplyChange(this TextDocument document)
{
return document?.State.CanApplyChange() ?? false;
}
public static bool CanApplyChange(this TextDocumentState document)
{
return document?.Services.GetService<IDocumentOperationService>().CanApplyChange ?? false;
}
public static bool SupportsDiagnostics(this TextDocument document)
{
return document?.State.SupportsDiagnostics() ?? false;
}
public static bool SupportsDiagnostics(this TextDocumentState document)
{
return document?.Services.GetService<IDocumentOperationService>().SupportDiagnostics ?? false;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
namespace Microsoft.CodeAnalysis.Host
{
/// <summary>
/// provide various operations for this document
///
/// I followed name from EditorOperation for now.
/// </summary>
internal interface IDocumentOperationService : IDocumentService
{
/// <summary>
/// document version of <see cref="Workspace.CanApplyChange(ApplyChangesKind)"/>
/// </summary>
bool CanApplyChange { get; }
/// <summary>
/// indicates whether this document supports diagnostics or not
/// </summary>
bool SupportDiagnostics { get; }
}
}
......@@ -15,7 +15,32 @@ internal sealed class DefaultTextDocumentServiceProvider : IDocumentServiceProvi
public TService GetService<TService>() where TService : class, IDocumentService
{
// right now, it doesn't implement much services but we expect it to implements all
// document services in future so that we can remove all if branches in feature code
// but just delegate work to default document services.
if (DocumentOperationService.Instance is TService service)
{
return service;
}
return default;
}
private class DocumentOperationService : IDocumentOperationService
{
public static readonly DocumentOperationService Instance = new DocumentOperationService();
// right now, we return CanApplyChange for all C# documents, but we probably want to return
// false for generated files such as resx files or winform designer files.
// right now, we have a bug where if user renames Resource.[ResourceName] we actually do the rename
// but not actually change resx files which in turn, break code since generated file go back to
// original next time someone changes resx files but reference left as renamed.
// with this, we now should be able to say no text changes for such files so that rename fails
// in those cases. if resx people adapt IDocumentService pattern, then they should be able to
// even support rename through IDynamicFileInfoProvider pattern once we address that in next
// iteration for razor. for now, we keep existing behavior
public bool CanApplyChange => true;
public bool SupportDiagnostics => true;
}
}
}
......@@ -1216,7 +1216,7 @@ private void CheckAllowedProjectChanges(ProjectChanges projectChanges)
}
if (!this.CanApplyChange(ApplyChangesKind.ChangeDocument)
&& projectChanges.GetChangedDocuments(true).Any())
&& projectChanges.GetChangedDocuments(onlyGetDocumentsWithTextChanges: true).Any())
{
throw new NotSupportedException(WorkspacesResources.Changing_documents_is_not_supported);
}
......@@ -1265,6 +1265,15 @@ private void CheckAllowedProjectChanges(ProjectChanges projectChanges)
{
throw new NotSupportedException(WorkspacesResources.Removing_analyzer_references_is_not_supported);
}
foreach (var documentId in projectChanges.GetChangedDocuments())
{
var document = projectChanges.OldProject.GetDocumentState(documentId) ?? projectChanges.NewProject.GetDocumentState(documentId);
if (!document.CanApplyChange())
{
throw new NotSupportedException(string.Format(WorkspacesResources.Changing_document_0_is_not_supported, document.FilePath ?? document.Name));
}
}
}
protected virtual bool CanApplyParseOptionChange(ParseOptions oldOptions, ParseOptions newOptions, Project project)
......
......@@ -503,6 +503,15 @@ internal class WorkspacesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Changing document &apos;{0}&apos; is not supported..
/// </summary>
internal static string Changing_document_0_is_not_supported {
get {
return ResourceManager.GetString("Changing_document_0_is_not_supported", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Changing document properties is not supported.
/// </summary>
......
......@@ -789,4 +789,7 @@
<data name="Visual_Basic_files" xml:space="preserve">
<value>Visual Basic files</value>
</data>
<data name="Changing_document_0_is_not_supported" xml:space="preserve">
<value>Changing document '{0}' is not supported.</value>
</data>
</root>
\ No newline at end of file
......@@ -12,6 +12,11 @@
<target state="new">C# files</target>
<note />
</trans-unit>
<trans-unit id="Changing_document_0_is_not_supported">
<source>Changing document '{0}' is not supported.</source>
<target state="new">Changing document '{0}' is not supported.</target>
<note />
</trans-unit>
<trans-unit id="Core_EditorConfig_Options">
<source>Core EditorConfig Options</source>
<target state="new">Core EditorConfig Options</target>
......
......@@ -12,6 +12,11 @@
<target state="new">C# files</target>
<note />
</trans-unit>
<trans-unit id="Changing_document_0_is_not_supported">
<source>Changing document '{0}' is not supported.</source>
<target state="new">Changing document '{0}' is not supported.</target>
<note />
</trans-unit>
<trans-unit id="Core_EditorConfig_Options">
<source>Core EditorConfig Options</source>
<target state="new">Core EditorConfig Options</target>
......
......@@ -12,6 +12,11 @@
<target state="new">C# files</target>
<note />
</trans-unit>
<trans-unit id="Changing_document_0_is_not_supported">
<source>Changing document '{0}' is not supported.</source>
<target state="new">Changing document '{0}' is not supported.</target>
<note />
</trans-unit>
<trans-unit id="Core_EditorConfig_Options">
<source>Core EditorConfig Options</source>
<target state="new">Core EditorConfig Options</target>
......
......@@ -12,6 +12,11 @@
<target state="new">C# files</target>
<note />
</trans-unit>
<trans-unit id="Changing_document_0_is_not_supported">
<source>Changing document '{0}' is not supported.</source>
<target state="new">Changing document '{0}' is not supported.</target>
<note />
</trans-unit>
<trans-unit id="Core_EditorConfig_Options">
<source>Core EditorConfig Options</source>
<target state="new">Core EditorConfig Options</target>
......
......@@ -12,6 +12,11 @@
<target state="new">C# files</target>
<note />
</trans-unit>
<trans-unit id="Changing_document_0_is_not_supported">
<source>Changing document '{0}' is not supported.</source>
<target state="new">Changing document '{0}' is not supported.</target>
<note />
</trans-unit>
<trans-unit id="Core_EditorConfig_Options">
<source>Core EditorConfig Options</source>
<target state="new">Core EditorConfig Options</target>
......
......@@ -12,6 +12,11 @@
<target state="new">C# files</target>
<note />
</trans-unit>
<trans-unit id="Changing_document_0_is_not_supported">
<source>Changing document '{0}' is not supported.</source>
<target state="new">Changing document '{0}' is not supported.</target>
<note />
</trans-unit>
<trans-unit id="Core_EditorConfig_Options">
<source>Core EditorConfig Options</source>
<target state="new">Core EditorConfig Options</target>
......
......@@ -12,6 +12,11 @@
<target state="new">C# files</target>
<note />
</trans-unit>
<trans-unit id="Changing_document_0_is_not_supported">
<source>Changing document '{0}' is not supported.</source>
<target state="new">Changing document '{0}' is not supported.</target>
<note />
</trans-unit>
<trans-unit id="Core_EditorConfig_Options">
<source>Core EditorConfig Options</source>
<target state="new">Core EditorConfig Options</target>
......
......@@ -12,6 +12,11 @@
<target state="new">C# files</target>
<note />
</trans-unit>
<trans-unit id="Changing_document_0_is_not_supported">
<source>Changing document '{0}' is not supported.</source>
<target state="new">Changing document '{0}' is not supported.</target>
<note />
</trans-unit>
<trans-unit id="Core_EditorConfig_Options">
<source>Core EditorConfig Options</source>
<target state="new">Core EditorConfig Options</target>
......
......@@ -12,6 +12,11 @@
<target state="new">C# files</target>
<note />
</trans-unit>
<trans-unit id="Changing_document_0_is_not_supported">
<source>Changing document '{0}' is not supported.</source>
<target state="new">Changing document '{0}' is not supported.</target>
<note />
</trans-unit>
<trans-unit id="Core_EditorConfig_Options">
<source>Core EditorConfig Options</source>
<target state="new">Core EditorConfig Options</target>
......
......@@ -12,6 +12,11 @@
<target state="new">C# files</target>
<note />
</trans-unit>
<trans-unit id="Changing_document_0_is_not_supported">
<source>Changing document '{0}' is not supported.</source>
<target state="new">Changing document '{0}' is not supported.</target>
<note />
</trans-unit>
<trans-unit id="Core_EditorConfig_Options">
<source>Core EditorConfig Options</source>
<target state="new">Core EditorConfig Options</target>
......
......@@ -12,6 +12,11 @@
<target state="new">C# files</target>
<note />
</trans-unit>
<trans-unit id="Changing_document_0_is_not_supported">
<source>Changing document '{0}' is not supported.</source>
<target state="new">Changing document '{0}' is not supported.</target>
<note />
</trans-unit>
<trans-unit id="Core_EditorConfig_Options">
<source>Core EditorConfig Options</source>
<target state="new">Core EditorConfig Options</target>
......
......@@ -12,6 +12,11 @@
<target state="new">C# files</target>
<note />
</trans-unit>
<trans-unit id="Changing_document_0_is_not_supported">
<source>Changing document '{0}' is not supported.</source>
<target state="new">Changing document '{0}' is not supported.</target>
<note />
</trans-unit>
<trans-unit id="Core_EditorConfig_Options">
<source>Core EditorConfig Options</source>
<target state="new">Core EditorConfig Options</target>
......
......@@ -12,6 +12,11 @@
<target state="new">C# files</target>
<note />
</trans-unit>
<trans-unit id="Changing_document_0_is_not_supported">
<source>Changing document '{0}' is not supported.</source>
<target state="new">Changing document '{0}' is not supported.</target>
<note />
</trans-unit>
<trans-unit id="Core_EditorConfig_Options">
<source>Core EditorConfig Options</source>
<target state="new">Core EditorConfig Options</target>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册