internal async Task OnProjectChangedAsync(IProjectVersionedValue <IProjectSubscriptionUpdate> e = null) { bool isDebuggable = await _launchProviders.Value.IsDebuggableAsync() .ConfigureAwait(false); if (isDebuggable) { // If we're already registered, the service no-ops _startupProjectsListService.AddProject(ref _projectGuid); } else { // If we're already unregistered, the service no-ops _startupProjectsListService.RemoveProject(ref _projectGuid); } }
public Task ApplyProjectEndBatchAsync(IProjectVersionedValue <IProjectSubscriptionUpdate> update, CancellationToken cancellationToken) { Requires.NotNull(update, nameof(update)); VerifyInitializedAndNotDisposed(); if (update.Value.ProjectChanges.TryGetValue(ProjectBuildRuleName, out IProjectChangeDescription projectChange) && projectChange.Difference.AnyChanges) { IComparable version = GetConfiguredProjectVersion(update); ProcessProjectUpdateHandlers(version, update, cancellationToken); } return(Task.CompletedTask); }
private async Task OnProjectChangedAsync(IProjectVersionedValue <IProjectSubscriptionUpdate> update) { if (IsDisposing || IsDisposed) { return; } await InitializeAsync().ConfigureAwait(false); // when TargetFrameworks or TargetFrameworkMoniker changes, reset subscriptions so that // any new configured projects are picked up if (HasTargetFrameworkChanged(update)) { await ResetSubscriptions().ConfigureAwait(false); } }
private async Task OnProjectChangedCoreAsync(IProjectVersionedValue <IProjectSubscriptionUpdate> e, RuleHandlerType handlerType) { // TODO: https://github.com/dotnet/roslyn-project-system/issues/353 await _commonServices.ThreadingService.SwitchToUIThread(); await _tasksService.LoadedProjectAsync(async() => { await HandleAsync(e, handlerType).ConfigureAwait(false); }); // If "TargetFrameworks" property has changed, we need to refresh the project context and subscriptions. if (HasTargetFrameworksChanged(e)) { await UpdateProjectContextAndSubscriptionsAsync().ConfigureAwait(false); } }
private void OnActiveConfigurationsChanged(IProjectVersionedValue <IConfigurationGroup <ConfiguredProject> > e) { if (IsDisposing || IsDisposed) { return; } // Clean up past subscriptions _designTimeBuildSubscriptionLink?.Dispose(); if (e.Value.Count > 0) { var sourceLinkOptions = new StandardRuleDataflowLinkOptions { RuleNames = s_designTimeBuildWatchedRules, PropagateCompletion = true }; var disposableBag = new DisposableBag(CancellationToken.None); // We are taking source blocks from multiple configured projects and creating a SyncLink to combine the sources. // The SyncLink will only publish data when the versions of the sources match. There is a problem with that. // The sources have some version components that will make this impossible to match across TFMs. We introduce a // intermediate block here that will remove those version components so that the synclink can actually sync versions. IEnumerable <ProjectDataSources.SourceBlockAndLink <IProjectValueVersions> > sourceBlocks = e.Value.Select( cp => { IReceivableSourceBlock <IProjectVersionedValue <IProjectSubscriptionUpdate> > sourceBlock = cp.Services.ProjectSubscription.JointRuleSource.SourceBlock; IPropagatorBlock <IProjectVersionedValue <IProjectSubscriptionUpdate>, IProjectVersionedValue <IProjectSubscriptionUpdate> > versionDropper = CreateVersionDropperBlock(); disposableBag.AddDisposable(sourceBlock.LinkTo(versionDropper, sourceLinkOptions)); return(versionDropper.SyncLinkOptions <IProjectValueVersions>(sourceLinkOptions)); }); Action <Tuple <ImmutableList <IProjectValueVersions>, TIdentityDictionary> > action = ProjectPropertyChanged; var target = new ActionBlock <Tuple <ImmutableList <IProjectValueVersions>, TIdentityDictionary> >(action); var targetLinkOptions = new DataflowLinkOptions { PropagateCompletion = true }; ImmutableList <ProjectDataSources.SourceBlockAndLink <IProjectValueVersions> > sourceBlocksAndCapabilitiesOptions = sourceBlocks.ToImmutableList() .Insert(0, _projectVsServices.Project.Capabilities.SourceBlock.SyncLinkOptions <IProjectValueVersions>()); disposableBag.AddDisposable(ProjectDataSources.SyncLinkTo(sourceBlocksAndCapabilitiesOptions, target, targetLinkOptions)); _designTimeBuildSubscriptionLink = disposableBag; } }
private async Task OnProjectChanged(IProjectVersionedValue <IProjectSubscriptionUpdate> e, RuleHandlerType handlerType) { if (IsDisposing || IsDisposed) { return; } await InitializeAsync().ConfigureAwait(false); // TODO: https://github.com/dotnet/roslyn-project-system/issues/353 await _commonServices.ThreadingService.SwitchToUIThread(); using (_tasksService.LoadedProject()) { await HandleAsync(e, handlerType).ConfigureAwait(false); } }
internal async Task OnInputsChangedAsync(IProjectVersionedValue <PackageRestoreUnconfiguredInput> e) { // No configurations - likely during project close if (e.Value.RestoreInfo is null) { return; } try { await RestoreAsync(e.Value.RestoreInfo); } finally { OnRestoreCompleted(e); } }
public async Task HandleAsync(IProjectVersionedValue <IProjectSubscriptionUpdate> e, IProjectChangeDescription projectChange) { Requires.NotNull(e, nameof(e)); Requires.NotNull(projectChange, nameof(projectChange)); IProjectChangeDiff diff = projectChange.Difference; foreach (string filePath in diff.RemovedItems) { // Item includes are always relative to csproj/vbproj string fullPath = _project.MakeRooted(filePath); RemoveSourceFile(fullPath); } if (diff.AddedItems.Count > 0 || diff.RenamedItems.Count > 0 || diff.ChangedItems.Count > 0) { // Make sure the tree matches the same version of the evaluation that we're handling IProjectTreeServiceState treeState = await _projectTree.TreeService.PublishTreeAsync(e.ToRequirements(), blockDuringLoadingTree : true) .ConfigureAwait(true); // TODO: https://github.com/dotnet/roslyn-project-system/issues/353 foreach (string filePath in diff.AddedItems) { string fullPath = _project.MakeRooted(filePath); AddSourceFile(fullPath, treeState); } foreach (KeyValuePair <string, string> filePaths in diff.RenamedItems) { string beforeFullPath = _project.MakeRooted(filePaths.Key); string afterFullPath = _project.MakeRooted(filePaths.Value); RemoveSourceFile(beforeFullPath); AddSourceFile(afterFullPath, treeState); } foreach (string filePath in diff.ChangedItems) { // We add and then remove ChangedItems to handle Linked metadata changes string fullPath = _project.MakeRooted(filePath); RemoveSourceFile(fullPath); AddSourceFile(fullPath); } } }
internal void OnChanged(IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectSubscriptionUpdate, IProjectItemSchema> > e) { lock (_stateLock) { if (_link == null) { // We've been unloaded, so don't update the state (which will be empty) return; } _state = _state.Update( jointRuleUpdate: e.Value.Item1, sourceItemsUpdate: e.Value.Item2, projectItemSchema: e.Value.Item3, configuredProjectVersion: e.DataSourceVersions[ProjectDataSources.ConfiguredProjectVersion]); } }
private Task ItemsChangedAsync(IProjectVersionedValue <IProjectCatalogSnapshot> snapshot) { SubmitTreeUpdateAsync( (treeSnapshot, configuredProjectExports, cancellationToken) => { var dependenciesNode = treeSnapshot.Value.Tree; if (!cancellationToken.IsCancellationRequested) { dependenciesNode = BuildTree(dependenciesNode, snapshot.Value, cancellationToken); } return(Task.FromResult(new TreeUpdateResult(dependenciesNode, false))); }); return(Task.CompletedTask); }
public async Task ApplyProjectBuildAsync(IProjectVersionedValue<IProjectSubscriptionUpdate> update, bool isActiveContext, CancellationToken cancellationToken) { Requires.NotNull(update, nameof(update)); VerifyInitializedAndNotDisposed(); IProjectChangeDescription projectChange = update.Value.ProjectChanges[ProjectBuildRuleName]; if (projectChange.Difference.AnyChanges) { IComparable version = GetConfiguredProjectVersion(update); ProcessOptions(projectChange.After); await ProcessCommandLineAsync(version, projectChange.Difference, isActiveContext, cancellationToken); ProcessProjectBuildFailure(projectChange.After); } }
private Task OnConfiguredProjectEvaluatedAsync(IProjectVersionedValue <IProjectSubscriptionUpdate> e) { // If "TargetFrameworks" property has changed, we need to refresh the project context and subscriptions. if (HasTargetFrameworksChanged()) { return(UpdateProjectContextAndSubscriptionsAsync()); } return(Task.CompletedTask); bool HasTargetFrameworksChanged() { // remember actual property value and compare return(e.Value.ProjectChanges.TryGetValue(ConfigurationGeneral.SchemaName, out IProjectChangeDescription projectChange) && (projectChange.Difference.ChangedProperties.Contains(ConfigurationGeneral.TargetFrameworkProperty) || projectChange.Difference.ChangedProperties.Contains(ConfigurationGeneral.TargetFrameworksProperty))); } }
private async Task OnProjectChangedCoreAsync(IProjectVersionedValue <IProjectSubscriptionUpdate> e, RuleHandlerType handlerType) { if (IsDisposing || IsDisposed) { return; } await _tasksService.LoadedProjectAsync(async() => { await HandleAsync(e, handlerType).ConfigureAwait(false); }); // If "TargetFramework" or "TargetFrameworks" property has changed, we need to refresh the project context and subscriptions. if (HasTargetFrameworksChanged(e)) { await UpdateProjectContextAndSubscriptionsAsync().ConfigureAwait(false); } }
private Task ProcessProjectEvaluationHandlersAsync(IComparable version, IProjectVersionedValue<IProjectSubscriptionUpdate> update, bool isActiveContext, CancellationToken cancellationToken) { foreach (ExportLifetimeContext<IWorkspaceContextHandler> handler in _handlers) { cancellationToken.ThrowIfCancellationRequested(); if (handler.Value is IProjectEvaluationHandler evaluationHandler) { IProjectChangeDescription projectChange = update.Value.ProjectChanges[evaluationHandler.ProjectEvaluationRule]; if (!projectChange.Difference.AnyChanges) continue; evaluationHandler.Handle(version, projectChange, isActiveContext, _logger); } } return Task.CompletedTask; }
[InlineData(false)] // Project Build public async Task OnProjectChangedAsync_PassesProjectUpdate(bool evaluation) { IProjectVersionedValue <IProjectSubscriptionUpdate> updateResult = null; void applyChanges(IProjectVersionedValue <IProjectSubscriptionUpdate> u, bool _, CancellationToken __) { updateResult = u; } var applyChangesToWorkspaceContext = evaluation ? IApplyChangesToWorkspaceContextFactory.ImplementApplyProjectEvaluationAsync(applyChanges) : IApplyChangesToWorkspaceContextFactory.ImplementApplyProjectBuildAsync(applyChanges); var instance = await CreateInitializedInstanceAsync(applyChangesToWorkspaceContext : applyChangesToWorkspaceContext); var update = IProjectVersionedValueFactory.CreateEmpty(); await instance.OnProjectChangedAsync(update, evaluation); Assert.Same(updateResult, update); }
private async Task ProcessDesignTimeInputs(IProjectVersionedValue <DesignTimeInputs> input) { DesignTimeInputs designTimeInputs = input.Value; IVsAsyncFileChangeEx?vsAsyncFileChangeEx = await _fileChangeService.GetValueAsync(); Assumes.Present(vsAsyncFileChangeEx); // we don't care about the difference between types of inputs, so we just construct one hashset for fast comparisons later var allFiles = new HashSet <string>(StringComparers.Paths); allFiles.AddRange(designTimeInputs.Inputs); allFiles.AddRange(designTimeInputs.SharedInputs); // Remove any files we're watching that we don't care about any more var removedFiles = new List <string>(); foreach ((string file, uint cookie) in _fileWatcherCookies) { if (!allFiles.Contains(file)) { await vsAsyncFileChangeEx.UnadviseFileChangeAsync(cookie); removedFiles.Add(file); } } foreach (string file in removedFiles) { _fileWatcherCookies.Remove(file); } // Now watch and output files that are new foreach (string file in allFiles) { if (!_fileWatcherCookies.ContainsKey(file)) { // We don't care about delete and add here, as they come through data flow, plus they are really bouncy - every file change is a Time, Del and Add event) uint cookie = await vsAsyncFileChangeEx.AdviseFileChangeAsync(file, _VSFILECHANGEFLAGS.VSFILECHG_Time | _VSFILECHANGEFLAGS.VSFILECHG_Size, sink : this); _fileWatcherCookies.Add(file, cookie); } } }
private async Task OnProjectChangedAsync( IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot> > e, RuleHandlerType handlerType) { if (IsDisposing || IsDisposed) { return; } await _tasksService.LoadedProjectAsync(async() => { if (_tasksService.UnloadCancellationToken.IsCancellationRequested) { return; } await HandleAsync(e, handlerType).ConfigureAwait(false); }); }
internal async Task OnProjectChangedAsync(IProjectVersionedValue <IProjectSubscriptionUpdate> e = null) { bool isDebuggable = await _launchProviders.Value.IsDebuggableAsync(); IVsStartupProjectsListService startupProjectsListService = await _startupProjectsListService.GetValueAsync(); Assumes.Present(startupProjectsListService); if (isDebuggable) { // If we're already registered, the service no-ops startupProjectsListService.AddProject(ref _projectGuid); } else { // If we're already unregistered, the service no-ops startupProjectsListService.RemoveProject(ref _projectGuid); } }
// Internal for testing internal async Task OnProjectChanged(IProjectVersionedValue <IProjectSubscriptionUpdate> update) { if (IsDisposing || IsDisposed) { return; } await CommonServices.TasksService.LoadedProjectAsync(async() => { await ExecuteWithLock(async() => { string mvcReferenceFullPath = null; var references = update.Value.CurrentState[ResolvedCompilationReference.SchemaName].Items; foreach (var reference in references) { if (reference.Key.EndsWith(MvcAssemblyFileName, StringComparison.OrdinalIgnoreCase)) { mvcReferenceFullPath = reference.Key; break; } } if (mvcReferenceFullPath == null) { // Ok we can't find an MVC version. Let's assume this project isn't using Razor then. await UpdateProjectUnsafeAsync(null).ConfigureAwait(false); return; } var version = GetAssemblyVersion(mvcReferenceFullPath); if (version == null) { // Ok we can't find an MVC version. Let's assume this project isn't using Razor then. await UpdateProjectUnsafeAsync(null).ConfigureAwait(false); return; } var configuration = FallbackRazorConfiguration.SelectConfiguration(version); var hostProject = new HostProject(CommonServices.UnconfiguredProject.FullPath, configuration); await UpdateProjectUnsafeAsync(hostProject).ConfigureAwait(false); }); }, registerFaultHandler : true); }
protected async Task ProjectRuleBlock_ChangedAsync(IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectCapabilitiesSnapshot> > projectSnapshot) { if (projectSnapshot.Value.Item1.CurrentState.TryGetValue(ProjectDebugger.SchemaName, out IProjectRuleSnapshot ruleSnapshot)) { ruleSnapshot.Properties.TryGetValue(ProjectDebugger.ActiveDebugProfileProperty, out string activeProfile); ILaunchSettings snapshot = CurrentSnapshot; if (snapshot == null || !LaunchProfile.IsSameProfileName(activeProfile, snapshot.ActiveProfile?.Name)) { // Updates need to be sequenced await _sequentialTaskQueue.ExecuteTask(async() => { using (ProjectCapabilitiesContext.CreateIsolatedContext(_commonProjectServices.Project, projectSnapshot.Value.Item2)) { await UpdateActiveProfileInSnapshotAsync(activeProfile); } }); } } }
internal void OnNamespaceImportChanged(IProjectVersionedValue <IProjectSubscriptionUpdate> e) { IProjectChangeDescription projectChange = e.Value.ProjectChanges[NamespaceImport.SchemaName]; if (projectChange.Difference.AnyChanges) { lock (_lock) { IOrderedEnumerable <string> sortedItems = projectChange.After.Items.Keys.OrderBy(s => s, StringComparer.OrdinalIgnoreCase); int newListCount = sortedItems.Count(); int oldListCount = _list.Count; int trackingIndex = 0; while (trackingIndex < oldListCount && trackingIndex < newListCount) { string incomingItem = sortedItems.ElementAt(trackingIndex); if (string.Compare(_list[trackingIndex], incomingItem, StringComparison.OrdinalIgnoreCase) == 0) { trackingIndex++; continue; } _list[trackingIndex] = incomingItem; trackingIndex++; } if (oldListCount == newListCount) { return; } else if (oldListCount < newListCount) { _list.AddRange(sortedItems.Skip(trackingIndex)); } else { _list.RemoveRange(trackingIndex, oldListCount - trackingIndex); } } } }
private async Task HandleAsync(IProjectVersionedValue <IProjectSubscriptionUpdate> update, RuleHandlerType handlerType) { var handlers = Handlers.Select(h => h.Value) .Where(h => h.HandlerType == handlerType); // We need to process the update within a lock to ensure that we do not release this context during processing. // TODO: Enable concurrent execution of updates themeselves, i.e. two separate invocations of HandleAsync // should be able to run concurrently. await ExecuteWithinLockAsync(async() => { // TODO: https://github.com/dotnet/roslyn-project-system/issues/353 await _commonServices.ThreadingService.SwitchToUIThread(); // Get the inner workspace project context to update for this change. var projectContextToUpdate = _currentAggregateProjectContext.GetInnerProjectContext(update.Value.ProjectConfiguration, out bool isActiveContext); if (projectContextToUpdate == null) { return; } // Broken design time builds sometimes cause updates with no project changes and sometimes cause updates with a project change that has no difference. // We handle the former case here, and the latter case is handled in the CommandLineItemHandler. if (update.Value.ProjectChanges.Count == 0) { if (handlerType == RuleHandlerType.DesignTimeBuild) { projectContextToUpdate.LastDesignTimeBuildSucceeded = false; } return; } foreach (var handler in handlers) { IProjectChangeDescription projectChange = update.Value.ProjectChanges[handler.RuleName]; if (handler.ReceiveUpdatesWithEmptyProjectChange || projectChange.Difference.AnyChanges) { await handler.HandleAsync(update, projectChange, projectContextToUpdate, isActiveContext).ConfigureAwait(true); } } }); }
public void ApplyDesignTime(IProjectVersionedValue <IProjectSubscriptionUpdate> update, bool isActiveContext, CancellationToken cancellationToken) { Requires.NotNull(update, nameof(update)); lock (SyncObject) { VerifyInitializedAndNotDisposed(); IProjectChangeDescription projectChange = update.Value.ProjectChanges[DesignTimeRuleName]; if (projectChange.Difference.AnyChanges) { IComparable version = GetConfiguredProjectVersion(update); ProcessOptions(projectChange.After); ProcessCommandLine(version, projectChange.Difference, isActiveContext, cancellationToken); ProcessDesignTimeBuildFailure(projectChange.After); } } }
private async Task HandleAsync(IProjectVersionedValue <IProjectSubscriptionUpdate> update, RuleHandlerType handlerType) { // We need to process the update within a lock to ensure that we do not release this context during processing. // TODO: Enable concurrent execution of updates themeselves, i.e. two separate invocations of HandleAsync // should be able to run concurrently. await ExecuteWithinLockAsync(async() => { // TODO: https://github.com/dotnet/roslyn-project-system/issues/353 await _commonServices.ThreadingService.SwitchToUIThread(); // Get the inner workspace project context to update for this change. IWorkspaceProjectContext projectContextToUpdate = _currentAggregateProjectContext.GetInnerProjectContext(update.Value.ProjectConfiguration, out bool isActiveContext); if (projectContextToUpdate == null) { return; } _languageServiceHandlerManager.Handle(update, handlerType, projectContextToUpdate, isActiveContext); }).ConfigureAwait(false); }
/// <summary> /// Handles changes to the references items in the project and updates the project tree. /// </summary> /// <param name="e">A description of the changes made to the project.</param> private void ProjectSubscriptionService_Changed(IProjectVersionedValue < Tuple <IProjectSubscriptionUpdate, IProjectCatalogSnapshot, IProjectSharedFoldersSnapshot> > e) { var dependenciesChange = ProcessDependenciesChanges(e.Value.Item1, e.Value.Item2); // process separatelly shared projects changes ProcessSharedProjectImportNodes(e.Value.Item3, dependenciesChange); // Apply dependencies changes to actual RootNode children collection // remove first nodes from actual RootNode dependenciesChange.RemovedNodes.ForEach(RootNode.RemoveChild); ProcessDuplicatedNodes(dependenciesChange); dependenciesChange.AddedNodes.ForEach(RootNode.AddChild); OnDependenciesChanged(dependenciesChange.GetDiff(), e); }
internal Task OnProjectChangedAsync(IProjectVersionedValue <IProjectSubscriptionUpdate>?_ = null) { return(_projectTasksService.LoadedProjectAsync(async() => { bool isDebuggable = await _launchProviders.Value.IsDebuggableAsync(); IVsStartupProjectsListService startupProjectsListService = await _startupProjectsListService.GetValueAsync(); if (isDebuggable) { // If we're already registered, the service no-ops startupProjectsListService.AddProject(ref _projectGuid); } else { // If we're already unregistered, the service no-ops startupProjectsListService.RemoveProject(ref _projectGuid); } })); }
private async Task OnProjectChangedAsync( IProjectVersionedValue <Tuple <IProjectSubscriptionUpdate, IProjectSharedFoldersSnapshot, IProjectCatalogSnapshot> > e) { if (IsDisposing || IsDisposed) { return; } EnsureInitialized(); await _tasksService.LoadedProjectAsync(async() => { if (_tasksService.UnloadCancellationToken.IsCancellationRequested) { return; } await HandleAsync(e); }); }
internal void ProcessDataflowChanges(IProjectVersionedValue <DesignTimeInputsDelta> obj) { // Cancel any in-progress queue processing _compilationCancellationSource?.Cancel(); DesignTimeInputsDelta delta = obj.Value; // add all of the changes to our queue _queue.Update(delta.ChangedInputs, delta.Inputs, delta.SharedInputs, delta.TempPEOutputPath); // Create a cancellation source so we can cancel the compilation if another message comes through _compilationCancellationSource = CancellationTokenSource.CreateLinkedTokenSource(_project.Services.ProjectAsynchronousTasks.UnloadCancellationToken); JoinableTask task = _scheduler.ScheduleAsyncTask(ProcessCompileQueueAsync, _compilationCancellationSource.Token); // For unit testing purposes, optionally block the thread until the task we scheduled is complete if (CompileSynchronously) { _threadingService.ExecuteSynchronously(() => task.Task); } }
private void ProcessProjectEvaluationHandlers(IComparable version, IProjectVersionedValue <IProjectSubscriptionUpdate> update, bool isActiveContext, CancellationToken cancellationToken) { foreach (ExportLifetimeContext <IWorkspaceContextHandler> handler in _handlers) { if (cancellationToken.IsCancellationRequested) { break; } if (handler.Value is IProjectEvaluationHandler evaluationHandler) { IProjectChangeDescription projectChange = update.Value.ProjectChanges[evaluationHandler.ProjectEvaluationRule]; if (!projectChange.Difference.AnyChanges) { continue; } evaluationHandler.Handle(version, projectChange, isActiveContext, _logger); } } }