diff --git a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/VisualStudioDesignerAttributeService.cs b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/VisualStudioDesignerAttributeService.cs index 6a4e73683407257806b6f7b191f219acc14c21df..7853750f73f0f59c2a55dad823eb4d4ddfaf35d7 100644 --- a/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/VisualStudioDesignerAttributeService.cs +++ b/src/VisualStudio/Core/Def/Implementation/DesignerAttribute/VisualStudioDesignerAttributeService.cs @@ -176,7 +176,7 @@ private void AddFilteredInfos(ImmutableArray data, ArrayB var cpsUpdateService = await GetUpdateServiceIfCpsProjectAsync(projectId, cancellationToken).ConfigureAwait(false); var task = cpsUpdateService == null ? NotifyLegacyProjectSystemAsync(projectId, data, cancellationToken) - : NotifyCpsProjectSystemAsync(cpsUpdateService, data, cancellationToken); + : NotifyCpsProjectSystemAsync(projectId, cpsUpdateService, data, cancellationToken); await task.ConfigureAwait(false); } @@ -242,12 +242,19 @@ private void AddFilteredInfos(ImmutableArray data, ArrayB } private async Task NotifyCpsProjectSystemAsync( + ProjectId projectId, IProjectItemDesignerTypeUpdateService updateService, IEnumerable data, CancellationToken cancellationToken) { cancellationToken.ThrowIfCancellationRequested(); + // We may have updates for many different configurations of the same logical project system project. + // However, the project system only associates designer attributes with one of those projects. So just drop + // the notifications for any sibling configurations. + if (!_workspace.IsPrimaryProject(projectId)) + return; + // Broadcast all the information about all the documents in parallel to CPS. using var _ = ArrayBuilder.GetInstance(out var tasks); diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/CPS/IWorkspaceProjectContext.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/CPS/IWorkspaceProjectContext.cs index d61a8ae818d85ab9c83a2e68148de5e1c76dec3e..a7ebc8ea13c4ae6190bc42bbd4a8e8e6ee73ee99 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/CPS/IWorkspaceProjectContext.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/CPS/IWorkspaceProjectContext.cs @@ -20,6 +20,14 @@ internal interface IWorkspaceProjectContext : IDisposable bool LastDesignTimeBuildSucceeded { get; set; } string BinOutputPath { get; set; } + /// + /// When this project is one of a multi-targeting group of projects, this value indicates whether or not this + /// particular project is the primary one. The primary project is responsible for certain things when reporting + /// data from Roslyn's individual projects back to the project system itself. For example, designer attributes + /// are only associated with the primary project, and should be skipped for other projects. + /// + bool IsPrimary { get; set; } + ProjectId Id { get; } // Options. diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProject.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProject.cs index 21ab4b5ec8a486f35cbf111013cf4aba18d04071..2c35aa9c091cb9423d4a2b9394f4405156628940 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProject.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioProject.cs @@ -16,6 +16,7 @@ using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Debugger.Interop; using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; using Microsoft.VisualStudio.PlatformUI; using Roslyn.Utilities; @@ -68,6 +69,13 @@ internal sealed class VisualStudioProject private string? _outputRefFilePath; private string? _defaultNamespace; + /// + /// If this project is the 'primary' project the project system cares about for a group of Roslyn projects that + /// correspond to different configurations of a single project system project. by + /// default. + /// + internal bool IsPrimary { get; set; } = true; + // Actual property values for 'RunAnalyzers' and 'RunAnalyzersDuringLiveAnalysis' properties from the project file. // Both these properties can be used to configure running analyzers, with RunAnalyzers overriding RunAnalyzersDuringLiveAnalysis. private bool? _runAnalyzersPropertyValue; diff --git a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs index 1f566b0d7defc3610eb3349c471cb55337e4175b..43100069747706e99c167ba15dd3c7feeaa2adbf 100644 --- a/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs +++ b/src/VisualStudio/Core/Def/Implementation/ProjectSystem/VisualStudioWorkspaceImpl.cs @@ -406,6 +406,23 @@ internal bool IsCPSProject(ProjectId projectId) return false; } + internal bool IsPrimaryProject(ProjectId projectId) + { + lock (_gate) + { + foreach (var (_, projects) in this._projectSystemNameToProjectsMap) + { + foreach (var project in projects) + { + if (project.Id == projectId) + return project.IsPrimary; + } + } + } + + return true; + } + protected override bool CanApplyCompilationOptionChange(CompilationOptions oldOptions, CompilationOptions newOptions, CodeAnalysis.Project project) => project.LanguageServices.GetRequiredService().CanApplyChange(oldOptions, newOptions); diff --git a/src/VisualStudio/Core/Impl/ProjectSystem/CPS/CPSProject_IWorkspaceProjectContext.cs b/src/VisualStudio/Core/Impl/ProjectSystem/CPS/CPSProject_IWorkspaceProjectContext.cs index 6dfaa271090b78419cd5989d9196de8fd9c13ad7..e8509e5e46a4e5214e1ebd47a741b5eb2411e4e2 100644 --- a/src/VisualStudio/Core/Impl/ProjectSystem/CPS/CPSProject_IWorkspaceProjectContext.cs +++ b/src/VisualStudio/Core/Impl/ProjectSystem/CPS/CPSProject_IWorkspaceProjectContext.cs @@ -14,8 +14,6 @@ using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel; using Microsoft.VisualStudio.LanguageServices.Implementation.TaskList; using Microsoft.VisualStudio.LanguageServices.ProjectSystem; -using Microsoft.VisualStudio.Shell; -using Microsoft.VisualStudio.Shell.Interop; using Roslyn.Utilities; namespace Microsoft.VisualStudio.LanguageServices.Implementation.ProjectSystem.CPS @@ -46,6 +44,12 @@ public string DisplayName set => _visualStudioProject.FilePath = value; } + public bool IsPrimary + { + get => _visualStudioProject.IsPrimary; + set => _visualStudioProject.IsPrimary = value; + } + public Guid Guid { get; diff --git a/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioProjectTests/PrimaryProjectTests.vb b/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioProjectTests/PrimaryProjectTests.vb new file mode 100644 index 0000000000000000000000000000000000000000..ef37eeb52212dd740f4e2dec0c98e04a21b81cc1 --- /dev/null +++ b/src/VisualStudio/Core/Test/ProjectSystemShim/VisualStudioProjectTests/PrimaryProjectTests.vb @@ -0,0 +1,50 @@ +' Licensed to the .NET Foundation under one or more agreements. +' The .NET Foundation licenses this file to you under the MIT license. +' See the LICENSE file in the project root for more information. + +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Test.Utilities +Imports Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim.Framework +Imports Roslyn.Test.Utilities + +Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ProjectSystemShim + <[UseExportProvider]> + Public Class PrimaryProjectTests + + Public Sub ProjectIsPrimaryByDefault() + Using environment = New TestEnvironment(GetType(TestDynamicFileInfoProviderThatProducesNoFiles)) + Dim project = environment.ProjectFactory.CreateAndAddToWorkspace( + "project", + LanguageNames.CSharp) + + Assert.True(project.IsPrimary) + End Using + End Sub + + + Public Sub ChangeProjectIsPrimary() + Using environment = New TestEnvironment(GetType(TestDynamicFileInfoProviderThatProducesNoFiles)) + Dim project = environment.ProjectFactory.CreateAndAddToWorkspace( + "project", + LanguageNames.CSharp) + + project.IsPrimary = False + Assert.False(project.IsPrimary) + End Using + End Sub + + + Public Sub ChangeProjectIsPrimaryBack() + Using environment = New TestEnvironment(GetType(TestDynamicFileInfoProviderThatProducesNoFiles)) + Dim project = environment.ProjectFactory.CreateAndAddToWorkspace( + "project", + LanguageNames.CSharp) + + project.IsPrimary = False + project.IsPrimary = True + Assert.True(project.IsPrimary) + End Using + End Sub + End Class +End Namespace +