// Copyright (c) .NET Foundation. 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.ComponentModel.Composition; using Microsoft.CodeAnalysis.Razor.ProjectSystem; using Microsoft.VisualStudio.Shell; using Microsoft.VisualStudio.Shell.Interop; using Microsoft.VisualStudio.Editor.Razor; using System.Runtime.InteropServices; using Microsoft.CodeAnalysis.Razor; namespace Microsoft.VisualStudio.LanguageServices.Razor { [Export(typeof(ProjectSnapshotChangeTrigger))] internal class VsSolutionUpdatesProjectSnapshotChangeTrigger : ProjectSnapshotChangeTrigger, IVsUpdateSolutionEvents2 { private readonly IServiceProvider _services; private readonly TextBufferProjectService _projectService; private ProjectSnapshotManagerBase _projectManager; [ImportingConstructor] public VsSolutionUpdatesProjectSnapshotChangeTrigger( [Import(typeof(SVsServiceProvider))] IServiceProvider services, VisualStudioWorkspaceAccessor workspaceAccessor) { if (services == null) { throw new ArgumentNullException(nameof(services)); } if (workspaceAccessor == null) { throw new ArgumentNullException(nameof(workspaceAccessor)); } _services = services; var languageServices = workspaceAccessor.Workspace.Services.GetLanguageServices(RazorLanguage.Name); _projectService = languageServices.GetRequiredService(); } // Internal for testing internal VsSolutionUpdatesProjectSnapshotChangeTrigger( IServiceProvider services, TextBufferProjectService projectService) { _services = services; _projectService = projectService; } public override void Initialize(ProjectSnapshotManagerBase projectManager) { _projectManager = projectManager; // Attach the event sink to solution update events. var solutionBuildManager = _services.GetService(typeof(SVsSolutionBuildManager)) as IVsSolutionBuildManager; if (solutionBuildManager != null) { // We expect this to be called only once. So we don't need to Unadvise. var hr = solutionBuildManager.AdviseUpdateSolutionEvents(this, out var cookie); Marshal.ThrowExceptionForHR(hr); } } public int UpdateSolution_Begin(ref int pfCancelUpdate) { return VSConstants.S_OK; } public int UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) { return VSConstants.S_OK; } public int UpdateSolution_StartUpdate(ref int pfCancelUpdate) { return VSConstants.S_OK; } public int UpdateSolution_Cancel() { return VSConstants.S_OK; } public int OnActiveProjectCfgChange(IVsHierarchy pIVsHierarchy) { return VSConstants.S_OK; } public int UpdateProjectCfg_Begin(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, ref int pfCancel) { return VSConstants.S_OK; } // This gets called when the project has finished building. public int UpdateProjectCfg_Done(IVsHierarchy pHierProj, IVsCfg pCfgProj, IVsCfg pCfgSln, uint dwAction, int fSuccess, int fCancel) { var projectName = _projectService.GetProjectName(pHierProj); var projectPath = _projectService.GetProjectPath(pHierProj); // Get the corresponding roslyn project by matching the project name and the project path. foreach (var project in _projectManager.Workspace.CurrentSolution.Projects) { if (string.Equals(projectName, project.Name, StringComparison.Ordinal) && string.Equals(projectPath, project.FilePath, StringComparison.OrdinalIgnoreCase)) { _projectManager.ProjectBuildComplete(project); break; } } return VSConstants.S_OK; } } }