提交 68015627 编写于 作者: C CyrusNajmabadi 提交者: GitHub

Merge pull request #19194 from CyrusNajmabadi/batchChanges

Batch 'designer attribute argument' changes.
// 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.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Remote;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Versions;
namespace Microsoft.CodeAnalysis.DesignerAttributes
{
internal struct DesignerAttributeResult
{
public string DesignerAttributeArgument;
public bool ContainsErrors;
public bool NotApplicable;
public DesignerAttributeResult(string designerAttributeArgument, bool containsErrors, bool notApplicable)
{
DesignerAttributeArgument = designerAttributeArgument;
ContainsErrors = containsErrors;
NotApplicable = notApplicable;
}
}
internal abstract class AbstractDesignerAttributeService : IDesignerAttributeService
{
protected abstract bool ProcessOnlyFirstTypeDefined();
protected abstract IEnumerable<SyntaxNode> GetAllTopLevelTypeDefined(SyntaxNode root);
protected abstract bool HasAttributesOrBaseTypeOrIsPartial(SyntaxNode typeNode);
public async Task<DesignerAttributeResult> ScanDesignerAttributesAsync(Document document, CancellationToken cancellationToken)
{
var workspace = document.Project.Solution.Workspace;
// same service run in both inproc and remote host, but remote host will not have RemoteHostClient service,
// so inproc one will always run
var client = await workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false);
if (client != null && !document.IsOpen())
{
// run designer attributes scanner on remote host
// we only run closed files to make open document to have better responsiveness.
// also we cache everything related to open files anyway, no saving by running
// them in remote host
return await ScanDesignerAttributesInRemoteHostAsync(client, document, cancellationToken).ConfigureAwait(false);
}
return await ScanDesignerAttributesInCurrentProcessAsync(document, cancellationToken).ConfigureAwait(false);
}
private async Task<DesignerAttributeResult> ScanDesignerAttributesInRemoteHostAsync(RemoteHostClient client, Document document, CancellationToken cancellationToken)
{
return await client.RunCodeAnalysisServiceOnRemoteHostAsync<DesignerAttributeResult>(
document.Project.Solution, nameof(IRemoteDesignerAttributeService.ScanDesignerAttributesAsync),
document.Id, cancellationToken).ConfigureAwait(false);
}
private async Task<DesignerAttributeResult> ScanDesignerAttributesInCurrentProcessAsync(Document document, CancellationToken cancellationToken)
public async Task<DesignerAttributeDocumentData> ScanDesignerAttributesAsync(Document document, CancellationToken cancellationToken)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(continueOnCapturedContext: false);
......@@ -83,7 +44,7 @@ private async Task<DesignerAttributeResult> ScanDesignerAttributesInCurrentProce
{
// The DesignerCategoryAttribute doesn't exist. either not applicable or
// no idea on design attribute status, just leave things as it is.
return new DesignerAttributeResult(designerAttributeArgument, documentHasError, notApplicable: true);
return new DesignerAttributeDocumentData(document.FilePath, designerAttributeArgument, documentHasError, notApplicable: true);
}
}
......@@ -114,7 +75,7 @@ private async Task<DesignerAttributeResult> ScanDesignerAttributesInCurrentProce
if (attribute != null && attribute.ConstructorArguments.Length == 1)
{
designerAttributeArgument = GetArgumentString(attribute.ConstructorArguments[0]);
return new DesignerAttributeResult(designerAttributeArgument, documentHasError, notApplicable: false);
return new DesignerAttributeDocumentData(document.FilePath, designerAttributeArgument, documentHasError, notApplicable: false);
}
}
}
......@@ -126,7 +87,7 @@ private async Task<DesignerAttributeResult> ScanDesignerAttributesInCurrentProce
}
}
return new DesignerAttributeResult(designerAttributeArgument, documentHasError, notApplicable: false);
return new DesignerAttributeDocumentData(document.FilePath, designerAttributeArgument, documentHasError, notApplicable: false);
}
private static string GetArgumentString(TypedConstant argument)
......@@ -140,5 +101,49 @@ private static string GetArgumentString(TypedConstant argument)
return ((string)argument.Value).Trim();
}
internal static async Task<ImmutableDictionary<string, DesignerAttributeDocumentData>> TryAnalyzeProjectInCurrentProcessAsync(
Project project, CancellationToken cancellationToken)
{
var projectVersion = await project.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false);
var semanticVersion = await project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false);
// Get whatever data we've current persisted.
var designerAttributeData = await DesignerAttributeProjectData.ReadAsync(
project, cancellationToken).ConfigureAwait(false);
// If we have no persisted data, or the persisted data is for a previous version of
// the project, then compute the results for the current project snapshot.
if (designerAttributeData == null ||
!VersionStamp.CanReusePersistedVersion(semanticVersion, designerAttributeData.SemanticVersion))
{
designerAttributeData = await ComputeAndPersistDesignerAttributeProjectDataAsync(
project, semanticVersion, cancellationToken).ConfigureAwait(false);
}
return designerAttributeData.PathToDocumentData;
}
private static async Task<DesignerAttributeProjectData> ComputeAndPersistDesignerAttributeProjectDataAsync(
Project project, VersionStamp semanticVersion, CancellationToken cancellationToken)
{
var service = project.LanguageServices.GetService<IDesignerAttributeService>();
var tasks = project.Documents.Select(
d => service.ScanDesignerAttributesAsync(d, cancellationToken)).ToArray();
await Task.WhenAll(tasks).ConfigureAwait(false);
var builder = ImmutableDictionary.CreateBuilder<string, DesignerAttributeDocumentData>();
foreach (var task in tasks)
{
var result = await task.ConfigureAwait(false);
builder[result.FilePath] = result;
}
var data = new DesignerAttributeProjectData(semanticVersion, builder.ToImmutable());
await data.PersistAsync(project, cancellationToken).ConfigureAwait(false);
return data;
}
}
}
}
\ No newline at end of file
// 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;
namespace Microsoft.CodeAnalysis.DesignerAttributes
{
/// <summary>
/// Marshalling type to pass designer attribute data to/from the OOP process.
/// </summary>
internal struct DesignerAttributeDocumentData : IEquatable<DesignerAttributeDocumentData>
{
public string FilePath;
public string DesignerAttributeArgument;
public bool ContainsErrors;
public bool NotApplicable;
public DesignerAttributeDocumentData(string filePath, string designerAttributeArgument, bool containsErrors, bool notApplicable)
{
FilePath = filePath;
DesignerAttributeArgument = designerAttributeArgument;
ContainsErrors = containsErrors;
NotApplicable = notApplicable;
}
public override bool Equals(object obj)
=> Equals((DesignerAttributeDocumentData)obj);
public bool Equals(DesignerAttributeDocumentData other)
{
return FilePath == other.FilePath &&
DesignerAttributeArgument == other.DesignerAttributeArgument &&
ContainsErrors == other.ContainsErrors &&
NotApplicable == other.NotApplicable;
}
// Currently no need for GetHashCode. If we end up using this as a key in a dictionary,
// feel free to add.
public override int GetHashCode()
=> throw new NotImplementedException();
}
}
\ No newline at end of file
// 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;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.DesignerAttributes
{
internal class DesignerAttributeProjectData
{
private const string StreamName = "<DesignerAttribute>";
private const string FormatVersion = "3";
public readonly VersionStamp SemanticVersion;
public readonly ImmutableDictionary<string, DesignerAttributeDocumentData> PathToDocumentData;
public DesignerAttributeProjectData(
VersionStamp semanticVersion, ImmutableDictionary<string, DesignerAttributeDocumentData> pathToDocumentData)
{
SemanticVersion = semanticVersion;
PathToDocumentData = pathToDocumentData;
}
public static async Task<DesignerAttributeProjectData> ReadAsync(
Project project, CancellationToken cancellationToken)
{
try
{
var solution = project.Solution;
var storageService = (IPersistentStorageService2)solution.Workspace.Services.GetService<IPersistentStorageService>();
using (var persistenceService = storageService.GetStorage(solution, checkBranchId: false))
using (var stream = await persistenceService.ReadStreamAsync(project, StreamName, cancellationToken).ConfigureAwait(false))
using (var reader = ObjectReader.TryGetReader(stream, cancellationToken))
{
if (reader != null)
{
var version = reader.ReadString();
if (version == FormatVersion)
{
var semanticVersion = VersionStamp.ReadFrom(reader);
var resultCount = reader.ReadInt32();
var builder = ImmutableDictionary.CreateBuilder<string, DesignerAttributeDocumentData>();
for (var i = 0; i < resultCount; i++)
{
var filePath = reader.ReadString();
var attribute = reader.ReadString();
var containsErrors = reader.ReadBoolean();
var notApplicable = reader.ReadBoolean();
builder[filePath] = new DesignerAttributeDocumentData(filePath, attribute, containsErrors, notApplicable);
}
return new DesignerAttributeProjectData(semanticVersion, builder.ToImmutable());
}
}
}
}
catch (Exception e) when (IOUtilities.IsNormalIOException(e))
{
// Storage APIs can throw arbitrary exceptions.
}
return null;
}
public async Task PersistAsync(Project project, CancellationToken cancellationToken)
{
try
{
var solution = project.Solution;
var storageService = (IPersistentStorageService2)solution.Workspace.Services.GetService<IPersistentStorageService>();
using (var storage = storageService.GetStorage(solution, checkBranchId: false))
using (var stream = SerializableBytes.CreateWritableStream())
using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken))
{
writer.WriteString(FormatVersion);
this.SemanticVersion.WriteTo(writer);
writer.WriteInt32(this.PathToDocumentData.Count);
foreach (var kvp in this.PathToDocumentData)
{
var result = kvp.Value;
writer.WriteString(result.FilePath);
writer.WriteString(result.DesignerAttributeArgument);
writer.WriteBoolean(result.ContainsErrors);
writer.WriteBoolean(result.NotApplicable);
}
stream.Position = 0;
await storage.WriteStreamAsync(project, StreamName, stream, cancellationToken).ConfigureAwait(false);
}
}
catch (Exception e) when (IOUtilities.IsNormalIOException(e))
{
// Storage APIs can throw arbitrary exceptions.
}
}
}
}
\ No newline at end of file
......@@ -8,6 +8,6 @@ namespace Microsoft.CodeAnalysis.DesignerAttributes
{
internal interface IDesignerAttributeService : ILanguageService
{
Task<DesignerAttributeResult> ScanDesignerAttributesAsync(Document document, CancellationToken cancellationToken);
Task<DesignerAttributeDocumentData> ScanDesignerAttributesAsync(Document document, CancellationToken cancellationToken);
}
}
......@@ -6,6 +6,6 @@ namespace Microsoft.CodeAnalysis.DesignerAttributes
{
internal interface IRemoteDesignerAttributeService
{
Task<DesignerAttributeResult> ScanDesignerAttributesAsync(DocumentId documentId);
Task<DesignerAttributeDocumentData[]> ScanDesignerAttributesAsync(ProjectId projectId);
}
}
......@@ -122,6 +122,8 @@
<Compile Include="Completion\Providers\AbstractCrefCompletionProvider.cs" />
<Compile Include="ConvertToInterpolatedString\AbstractConvertConcatenationToInterpolatedStringRefactoringProvider.cs" />
<Compile Include="DesignerAttributes\AbstractDesignerAttributeService.cs" />
<Compile Include="DesignerAttributes\DesignerAttributeProjectData.cs" />
<Compile Include="DesignerAttributes\DesignerAttributeDocumentData.cs" />
<Compile Include="DesignerAttributes\IDesignerAttributeService.cs" />
<Compile Include="DesignerAttributes\IRemoteDesignerAttributeService.cs" />
<Compile Include="Diagnostics\Analyzers\NamingStyleDiagnosticAnalyzerBase.cs" />
......
// 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;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis;
......@@ -14,7 +16,6 @@
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.TestHooks;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.CodeAnalysis.Versions;
using Microsoft.VisualStudio.Designer.Interfaces;
using Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem;
using Microsoft.VisualStudio.Shell.Interop;
......@@ -27,7 +28,6 @@ internal partial class DesignerAttributeIncrementalAnalyzer : ForegroundThreadAf
private readonly IForegroundNotificationService _notificationService;
private readonly IServiceProvider _serviceProvider;
private readonly DesignerAttributeState _state;
private readonly IAsynchronousOperationListener _listener;
/// <summary>
......@@ -37,6 +37,13 @@ internal partial class DesignerAttributeIncrementalAnalyzer : ForegroundThreadAf
/// </summary>
private IVSMDDesignerService _dotNotAccessDirectlyDesigner;
/// <summary>
/// Keep track of the last results we reported to VS. We can use this to diff future results
/// to report only what actually changed.
/// </summary>
private readonly ConcurrentDictionary<ProjectId, ImmutableDictionary<string, DesignerAttributeDocumentData>> _lastReportedProjectData =
new ConcurrentDictionary<ProjectId, ImmutableDictionary<string, DesignerAttributeDocumentData>>();
public DesignerAttributeIncrementalAnalyzer(
IServiceProvider serviceProvider,
IForegroundNotificationService notificationService,
......@@ -48,13 +55,6 @@ internal partial class DesignerAttributeIncrementalAnalyzer : ForegroundThreadAf
_notificationService = notificationService;
_listener = new AggregateAsynchronousOperationListener(asyncListeners, FeatureAttribute.DesignerAttribute);
_state = new DesignerAttributeState();
}
public Task DocumentResetAsync(Document document, CancellationToken cancellationToken)
{
_state.Remove(document.Id);
return _state.PersistAsync(document, new Data(VersionStamp.Default, VersionStamp.Default, designerAttributeArgument: null), cancellationToken);
}
public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs e)
......@@ -62,133 +62,175 @@ public bool NeedsReanalysisOnOptionChanged(object sender, OptionChangedEventArgs
return false;
}
public async Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken)
public async Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken)
{
Contract.ThrowIfFalse(document.IsFromPrimaryBranch());
Contract.ThrowIfFalse(project.IsFromPrimaryBranch());
cancellationToken.ThrowIfCancellationRequested();
if (!document.Project.Solution.Workspace.Options.GetOption(InternalFeatureOnOffOptions.DesignerAttributes))
var vsWorkspace = project.Solution.Workspace as VisualStudioWorkspaceImpl;
if (vsWorkspace == null)
{
return;
}
// use tree version so that things like compiler option changes are considered
var textVersion = await document.GetTextVersionAsync(cancellationToken).ConfigureAwait(false);
var projectVersion = await document.Project.GetDependentVersionAsync(cancellationToken).ConfigureAwait(false);
var semanticVersion = await document.Project.GetDependentSemanticVersionAsync(cancellationToken).ConfigureAwait(false);
var existingData = await _state.TryGetExistingDataAsync(document, cancellationToken).ConfigureAwait(false);
if (existingData != null)
if (!vsWorkspace.Options.GetOption(InternalFeatureOnOffOptions.DesignerAttributes))
{
// check whether we can use the data as it is (can happen when re-using persisted data from previous VS session)
if (CheckVersions(document, textVersion, projectVersion, semanticVersion, existingData))
{
RegisterDesignerAttribute(document, existingData.DesignerAttributeArgument);
return;
}
return;
}
var result = await ScanDesignerAttributesOnRemoteHostIfPossibleAsync(document, cancellationToken).ConfigureAwait(false);
if (result.NotApplicable)
#if false
// CPS projects do not support designer attributes. So we just skip these projects entirely.
var isCPSProject = await Task.Factory.StartNew(
() => vsWorkspace.IsCPSProject(project),
cancellationToken,
TaskCreationOptions.None,
this.ForegroundTaskScheduler).ConfigureAwait(false);
if (isCPSProject)
{
_state.Remove(document.Id);
return;
}
#endif
// we checked all types in the document, but couldn't find designer attribute, but we can't say this document doesn't have designer attribute
// if the document also contains some errors.
var designerAttributeArgumentOpt = result.ContainsErrors ? new Optional<string>() : new Optional<string>(result.DesignerAttributeArgument);
await RegisterDesignerAttributeAndSaveStateAsync(document, textVersion, semanticVersion, designerAttributeArgumentOpt, cancellationToken).ConfigureAwait(false);
}
private async Task<DesignerAttributeResult> ScanDesignerAttributesOnRemoteHostIfPossibleAsync(Document document, CancellationToken cancellationToken)
{
var service = document.GetLanguageService<IDesignerAttributeService>();
if (service == null)
// Try to compute this data in the remote process. If that fails, then compute
// the results in the local process.
var pathToResult = await TryAnalyzeProjectInRemoteProcessAsync(project, cancellationToken).ConfigureAwait(false);
if (pathToResult == null)
{
return new DesignerAttributeResult(designerAttributeArgument: null, containsErrors: true, notApplicable: true);
pathToResult = await AbstractDesignerAttributeService.TryAnalyzeProjectInCurrentProcessAsync(
project, cancellationToken).ConfigureAwait(false);
}
return await service.ScanDesignerAttributesAsync(document, cancellationToken).ConfigureAwait(false);
cancellationToken.ThrowIfCancellationRequested();
// Once we get the current data, diff it and report the results to VS.
RegisterDesignerAttributes(project, pathToResult);
}
private bool CheckVersions(
Document document, VersionStamp textVersion, VersionStamp dependentProjectVersion, VersionStamp dependentSemanticVersion, Data existingData)
private async Task<ImmutableDictionary<string, DesignerAttributeDocumentData>> TryAnalyzeProjectInRemoteProcessAsync(Project project, CancellationToken cancellationToken)
{
// first check full version to see whether we can reuse data in same session, if we can't, check timestamp only version to see whether
// we can use it cross-session.
return document.CanReusePersistedTextVersion(textVersion, existingData.TextVersion) &&
document.Project.CanReusePersistedDependentSemanticVersion(dependentProjectVersion, dependentSemanticVersion, existingData.SemanticVersion);
using (var session = await TryGetRemoteSessionAsync(project.Solution, cancellationToken).ConfigureAwait(false))
{
if (session == null)
{
return null;
}
var serializedResults = await session.InvokeAsync<DesignerAttributeDocumentData[]>(
nameof(IRemoteDesignerAttributeService.ScanDesignerAttributesAsync), project.Id).ConfigureAwait(false);
var data = serializedResults.ToImmutableDictionary(kvp => kvp.FilePath);
return data;
}
}
private async Task RegisterDesignerAttributeAndSaveStateAsync(
Document document, VersionStamp textVersion, VersionStamp semanticVersion, Optional<string> designerAttributeArgumentOpt, CancellationToken cancellationToken)
private static async Task<RemoteHostClient.Session> TryGetRemoteSessionAsync(
Solution solution, CancellationToken cancellationToken)
{
if (!designerAttributeArgumentOpt.HasValue)
var client = await solution.Workspace.TryGetRemoteHostClientAsync(cancellationToken).ConfigureAwait(false);
if (client == null)
{
// no value means it couldn't determine whether this document has designer attribute or not.
// one of such case is when base type is error type.
return;
return null;
}
var data = new Data(textVersion, semanticVersion, designerAttributeArgumentOpt.Value);
await _state.PersistAsync(document, data, cancellationToken).ConfigureAwait(false);
RegisterDesignerAttribute(document, designerAttributeArgumentOpt.Value);
return await client.TryCreateCodeAnalysisServiceSessionAsync(
solution, cancellationToken).ConfigureAwait(false);
}
private void RegisterDesignerAttribute(Document document, string designerAttributeArgument)
private void RegisterDesignerAttributes(
Project project, ImmutableDictionary<string, DesignerAttributeDocumentData> pathToResult)
{
var workspace = document.Project.Solution.Workspace as VisualStudioWorkspaceImpl;
if (workspace == null)
// Diff this result against the last result we reported for this project.
// If there are any changes report them all at once to VS.
var lastPathToResult = _lastReportedProjectData.GetOrAdd(
project.Id, ImmutableDictionary<string, DesignerAttributeDocumentData>.Empty);
_lastReportedProjectData[project.Id] = pathToResult;
var difference = GetDifference(lastPathToResult, pathToResult);
if (difference.Count == 0)
{
return;
}
var documentId = document.Id;
_notificationService.RegisterNotification(() =>
{
var vsDocument = workspace.GetHostDocument(documentId);
if (vsDocument == null)
foreach (var document in project.Documents)
{
return;
if (difference.TryGetValue(document.FilePath, out var result))
{
RegisterDesignerAttribute(document, result.DesignerAttributeArgument);
}
}
}, _listener.BeginAsyncOperation("RegisterDesignerAttribute"));
}
uint itemId = vsDocument.GetItemId();
if (itemId == (uint)VSConstants.VSITEMID.Nil)
{
// it is no longer part of the solution
return;
}
private ImmutableDictionary<string, DesignerAttributeDocumentData> GetDifference(
ImmutableDictionary<string, DesignerAttributeDocumentData> oldFileToResult,
ImmutableDictionary<string, DesignerAttributeDocumentData> newFileToResult)
{
var difference = ImmutableDictionary.CreateBuilder<string, DesignerAttributeDocumentData>();
if (ErrorHandler.Succeeded(vsDocument.Project.Hierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_ItemSubType, out var currentValue)))
foreach (var newKvp in newFileToResult)
{
// 1) If this result is for a new document. We always need to report it
// 2) If both the old and new data have this result, then report it if it is different.
var filePath = newKvp.Key;
var newResult = newKvp.Value;
if (!oldFileToResult.TryGetValue(filePath, out var oldResult) ||
!newResult.Equals(oldResult))
{
var currentStringValue = string.IsNullOrEmpty(currentValue as string) ? null : (string)currentValue;
if (string.Equals(currentStringValue, designerAttributeArgument, StringComparison.OrdinalIgnoreCase))
{
// PERF: Avoid sending the message if the project system already has the current value.
return;
}
difference.Add(filePath, newResult);
}
}
return difference.ToImmutable();
}
private void RegisterDesignerAttribute(Document document, string designerAttributeArgument)
{
var workspace = (VisualStudioWorkspaceImpl)document.Project.Solution.Workspace;
var vsDocument = workspace.GetHostDocument(document.Id);
if (vsDocument == null)
{
return;
}
uint itemId = vsDocument.GetItemId();
if (itemId == (uint)VSConstants.VSITEMID.Nil)
{
// it is no longer part of the solution
return;
}
try
if (ErrorHandler.Succeeded(vsDocument.Project.Hierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_ItemSubType, out var currentValue)))
{
var currentStringValue = string.IsNullOrEmpty(currentValue as string) ? null : (string)currentValue;
if (string.Equals(currentStringValue, designerAttributeArgument, StringComparison.OrdinalIgnoreCase))
{
var designer = GetDesignerFromForegroundThread();
if (designer != null)
{
designer.RegisterDesignViewAttribute(vsDocument.Project.Hierarchy, (int)itemId, dwClass: 0, pwszAttributeValue: designerAttributeArgument);
}
// PERF: Avoid sending the message if the project system already has the current value.
return;
}
catch
}
try
{
var designer = GetDesignerFromForegroundThread();
if (designer != null)
{
// DevDiv # 933717
// turns out RegisterDesignViewAttribute can throw in certain cases such as a file failed to be checked out by source control
// or IVSHierarchy failed to set a property for this project
//
// just swallow it. don't crash VS.
designer.RegisterDesignViewAttribute(vsDocument.Project.Hierarchy, (int)itemId, dwClass: 0, pwszAttributeValue: designerAttributeArgument);
}
}, _listener.BeginAsyncOperation("RegisterDesignerAttribute"));
}
catch
{
// DevDiv # 933717
// turns out RegisterDesignViewAttribute can throw in certain cases such as a file failed to be checked out by source control
// or IVSHierarchy failed to set a property for this project
//
// just swallow it. don't crash VS.
}
}
private IVSMDDesignerService GetDesignerFromForegroundThread()
......@@ -204,54 +246,34 @@ private IVSMDDesignerService GetDesignerFromForegroundThread()
return _dotNotAccessDirectlyDesigner;
}
public void RemoveDocument(DocumentId documentId)
{
_state.Remove(documentId);
}
#region unused
private class Data
{
public readonly VersionStamp TextVersion;
public readonly VersionStamp SemanticVersion;
public readonly string DesignerAttributeArgument;
public Data(VersionStamp textVersion, VersionStamp semanticVersion, string designerAttributeArgument)
{
this.TextVersion = textVersion;
this.SemanticVersion = semanticVersion;
this.DesignerAttributeArgument = designerAttributeArgument;
}
}
#region unused
public Task NewSolutionSnapshotAsync(Solution solution, CancellationToken cancellationToken)
{
return SpecializedTasks.EmptyTask;
}
=> SpecializedTasks.EmptyTask;
public Task AnalyzeDocumentAsync(Document document, SyntaxNode bodyOpt, InvocationReasons reasons, CancellationToken cancellationToken)
=> SpecializedTasks.EmptyTask;
public Task DocumentOpenAsync(Document document, CancellationToken cancellationToken)
{
return SpecializedTasks.EmptyTask;
}
=> SpecializedTasks.EmptyTask;
public Task DocumentCloseAsync(Document document, CancellationToken cancellationToken)
{
return SpecializedTasks.EmptyTask;
}
=> SpecializedTasks.EmptyTask;
public Task DocumentResetAsync(Document document, CancellationToken cancellationToken)
=> SpecializedTasks.EmptyTask;
public Task AnalyzeSyntaxAsync(Document document, InvocationReasons reasons, CancellationToken cancellationToken)
{
return SpecializedTasks.EmptyTask;
}
=> SpecializedTasks.EmptyTask;
public Task AnalyzeProjectAsync(Project project, bool semanticsChanged, InvocationReasons reasons, CancellationToken cancellationToken)
public void RemoveDocument(DocumentId documentId)
{
return SpecializedTasks.EmptyTask;
}
public void RemoveProject(ProjectId projectId)
{
}
#endregion
#endregion
}
}
}
\ No newline at end of file
......@@ -14,7 +14,7 @@ namespace Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribu
[ExportIncrementalAnalyzerProvider(Name, new[] { WorkspaceKind.Host }), Shared]
internal class DesignerAttributeIncrementalAnalyzerProvider : IIncrementalAnalyzerProvider
{
public const string Name = "DesignerAttributeIncrementalAnalyzerProvider";
public const string Name = nameof(DesignerAttributeIncrementalAnalyzerProvider);
private readonly IServiceProvider _serviceProvider;
private readonly IForegroundNotificationService _notificationService;
......@@ -36,4 +36,4 @@ public IIncrementalAnalyzer CreateIncrementalAnalyzer(Workspace workspace)
return new DesignerAttributeIncrementalAnalyzer(_serviceProvider, _notificationService, _asyncListeners);
}
}
}
}
\ No newline at end of file
// 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;
using System.IO;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.SolutionCrawler;
using Microsoft.CodeAnalysis.SolutionCrawler.State;
using Roslyn.Utilities;
namespace Microsoft.VisualStudio.LanguageServices.Implementation.DesignerAttribute
{
internal partial class DesignerAttributeIncrementalAnalyzer : IIncrementalAnalyzer
{
private class DesignerAttributeState : AbstractDocumentAnalyzerState<Data>
{
private const string FormatVersion = "1";
protected override string StateName
{
get
{
return "<DesignerAttribute>";
}
}
protected override int GetCount(Data data)
{
return 1;
}
protected override Data TryGetExistingData(Stream stream, Document value, CancellationToken cancellationToken)
{
using (var reader = ObjectReader.TryGetReader(stream))
{
if (reader != null)
{
var format = reader.ReadString();
if (string.Equals(format, FormatVersion, StringComparison.InvariantCulture))
{
var textVersion = VersionStamp.ReadFrom(reader);
var dataVersion = VersionStamp.ReadFrom(reader);
var designerAttributeArgument = reader.ReadString();
return new Data(textVersion, dataVersion, designerAttributeArgument);
}
}
}
return null;
}
protected override void WriteTo(Stream stream, Data data, CancellationToken cancellationToken)
{
using (var writer = new ObjectWriter(stream, cancellationToken: cancellationToken))
{
writer.WriteString(FormatVersion);
data.TextVersion.WriteTo(writer);
data.SemanticVersion.WriteTo(writer);
writer.WriteString(data.DesignerAttributeArgument);
}
}
}
}
}
......@@ -187,7 +187,12 @@ internal override bool CanChangeActiveContextDocument
}
internal override bool CanRenameFilesDuringCodeActions(CodeAnalysis.Project project)
=> !IsCPSProject(project);
internal bool IsCPSProject(CodeAnalysis.Project project)
{
_foregroundObject.Value.AssertIsForeground();
if (this.TryGetHierarchy(project.Id, out var hierarchy))
{
// Currently renaming files in CPS projects (i.e. .Net Core) doesn't work proprey.
......@@ -195,10 +200,10 @@ internal override bool CanRenameFilesDuringCodeActions(CodeAnalysis.Project proj
// (despite the DTE interfaces being synchronous). So Roslyn calls the methods
// expecting the changes to happen immediately. Because they are deferred in CPS
// this causes problems.
return !hierarchy.IsCapabilityMatch("CPS");
return hierarchy.IsCapabilityMatch("CPS");
}
return true;
return false;
}
protected override bool CanApplyParseOptionChange(ParseOptions oldOptions, ParseOptions newOptions, CodeAnalysis.Project project)
......
......@@ -493,7 +493,6 @@
<Compile Include="Implementation\Debugging\IProximityExpressionsService.cs" />
<Compile Include="Implementation\DesignerAttribute\DesignerAttributeIncrementalAnalyzer.cs" />
<Compile Include="Implementation\DesignerAttribute\DesignerAttributeIncrementalAnalyzerProvider.cs" />
<Compile Include="Implementation\DesignerAttribute\DesignerAttributeState.cs" />
<Compile Include="Implementation\Diagnostics\VisualStudioWorkspaceDiagnosticAnalyzerProviderService.cs" />
<Compile Include="Implementation\Diagnostics\VisualStudioDiagnosticAnalyzerService.cs" />
<Compile Include="Implementation\Diagnostics\IVisualStudioDiagnosticAnalyzerService.cs" />
......
......@@ -92,11 +92,11 @@ class Test { }";
var solution = workspace.CurrentSolution;
var result = await client.RunCodeAnalysisServiceOnRemoteHostAsync<DesignerAttributeResult>(
var result = await client.RunCodeAnalysisServiceOnRemoteHostAsync<DesignerAttributeDocumentData[]>(
solution, nameof(IRemoteDesignerAttributeService.ScanDesignerAttributesAsync),
solution.Projects.First().DocumentIds.First(), CancellationToken.None);
solution.Projects.First().Id, CancellationToken.None);
Assert.Equal(result.DesignerAttributeArgument, "Form");
Assert.Equal(result[0].DesignerAttributeArgument, "Form");
}
}
......
......@@ -167,6 +167,14 @@ private bool SolutionSizeAboveThreshold(Solution solution)
return true;
}
var workspace = solution.Workspace;
if (workspace.Kind == WorkspaceKind.RemoteWorkspace ||
workspace.Kind == WorkspaceKind.RemoteTemporaryWorkspace)
{
// Storage is always available in the remote server.
return true;
}
if (_solutionSizeTracker == null)
{
return false;
......
......@@ -371,5 +371,6 @@ internal enum FunctionId
CodeLens_FindReferenceMethodsAsync,
CodeLens_GetFullyQualifiedName,
RemoteHostClientService_Restarted,
CodeAnalysisService_GetDesignerAttributesAsync,
}
}
......@@ -17,5 +17,6 @@ public static class WorkspaceKind
internal const string AnyCodeRoslynWorkspace = nameof(AnyCodeRoslynWorkspace);
internal const string RemoteWorkspace = nameof(RemoteWorkspace);
internal const string RemoteTemporaryWorkspace = nameof(RemoteTemporaryWorkspace);
}
}
......@@ -11,10 +11,8 @@ namespace Microsoft.CodeAnalysis.Remote
/// </summary>
internal class TemporaryWorkspace : Workspace
{
public const string WorkspaceKind_TemporaryWorkspace = "TemporaryWorkspace";
public TemporaryWorkspace(Solution solution)
: base(RoslynServices.HostServices, workspaceKind: TemporaryWorkspace.WorkspaceKind_TemporaryWorkspace)
: base(RoslynServices.HostServices, workspaceKind: WorkspaceKind.RemoteTemporaryWorkspace)
{
Options = Options.WithChangedOption(CacheOptions.RecoverableTreeLengthThreshold, 0);
......@@ -22,7 +20,7 @@ public TemporaryWorkspace(Solution solution)
}
public TemporaryWorkspace(SolutionInfo solutionInfo)
: base(RoslynServices.HostServices, workspaceKind: TemporaryWorkspace.WorkspaceKind_TemporaryWorkspace)
: base(RoslynServices.HostServices, workspaceKind: WorkspaceKind.RemoteTemporaryWorkspace)
{
Options = Options.WithChangedOption(CacheOptions.RecoverableTreeLengthThreshold, 0);
......
......@@ -12,7 +12,7 @@
namespace Microsoft.CodeAnalysis.Remote
{
[ExportWorkspaceServiceFactory(typeof(IOptionService), TemporaryWorkspace.WorkspaceKind_TemporaryWorkspace), Shared]
[ExportWorkspaceServiceFactory(typeof(IOptionService), WorkspaceKind.RemoteTemporaryWorkspace), Shared]
internal class TemporaryWorkspaceOptionsServiceFactory : IWorkspaceServiceFactory
{
private readonly ImmutableArray<Lazy<IOptionProvider>> _providers;
......
// 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;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.DesignerAttributes;
using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Shared.Extensions;
using RoslynLogger = Microsoft.CodeAnalysis.Internal.Log.Logger;
namespace Microsoft.CodeAnalysis.Remote
......@@ -18,35 +16,16 @@ internal partial class CodeAnalysisService : IRemoteDesignerAttributeService
///
/// This will be called by ServiceHub/JsonRpc framework
/// </summary>
public async Task<DesignerAttributeResult> ScanDesignerAttributesAsync(DocumentId documentId)
public async Task<DesignerAttributeDocumentData[]> ScanDesignerAttributesAsync(ProjectId projectId)
{
using (RoslynLogger.LogBlock(FunctionId.CodeAnalysisService_GetTodoCommentsAsync, documentId.ProjectId.DebugName, CancellationToken))
using (RoslynLogger.LogBlock(FunctionId.CodeAnalysisService_GetDesignerAttributesAsync, projectId.DebugName, CancellationToken))
{
try
{
var solution = await GetSolutionAsync().ConfigureAwait(false);
var document = solution.GetDocument(documentId);
var solution = await GetSolutionAsync().ConfigureAwait(false);
var project = solution.GetProject(projectId);
var data = await AbstractDesignerAttributeService.TryAnalyzeProjectInCurrentProcessAsync(
project, CancellationToken).ConfigureAwait(false);
var service = document.GetLanguageService<IDesignerAttributeService>();
if (service != null)
{
// todo comment service supported
return await service.ScanDesignerAttributesAsync(document, CancellationToken).ConfigureAwait(false);
}
}
catch (IOException)
{
// stream to send over result has closed before we
// had chance to check cancellation
}
catch (OperationCanceledException)
{
// rpc connection has closed.
// this can happen if client side cancelled the
// operation
}
return new DesignerAttributeResult(designerAttributeArgument: null, containsErrors: true, notApplicable: true);
return data.Values.ToArray();
}
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册