private void ProgressUpdated() { // we prefer showing evaluating if progress is flipping between evaluate and pause // in short period of time. var forceUpdate = _lastShownProgressStatus == ProgressStatus.Paused && _lastProgressStatus == ProgressStatus.Evaluating; var current = DateTimeOffset.UtcNow; if (!forceUpdate && current - _lastTimeReported < s_minimumInterval) { // make sure we are not flooding UI. // this is just presentation, fine to not updating UI right away especially since // at the end, this notification will go away automatically // but to make UI to be updated to right status eventually if task takes long time to finish // we enqueue refresh task. EnqueueRefresh(); return; } _lastShownProgressStatus = _lastProgressStatus; _lastTimeReported = current; ChangeProgress(_taskHandler, GetMessage()); string GetMessage() { var statusMessage = (_lastProgressStatus == ProgressStatus.Paused) ? ServicesVSResources.Paused_0_tasks_in_queue : ServicesVSResources.Evaluating_0_tasks_in_queue; return(string.Format(statusMessage, _lastPendingItemCount)); } void EnqueueRefresh() { if (_resettableDelay != null) { _resettableDelay.Reset(); return; } _resettableDelay = new ResettableDelay((int)s_minimumInterval.TotalMilliseconds, AsynchronousOperationListenerProvider.NullListener); _resettableDelay.Task.SafeContinueWith(_ => { _resettableDelay = null; ProgressUpdated(); }, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); } }
private void OnWorkspaceChanged(object sender, WorkspaceChangeEventArgs e) { // workspace event is serialized events. and reset delay only get updated here if (!_resettableDelay.Task.IsCompleted) { _resettableDelay.Reset(); return; } var delay = new ResettableDelay((int)s_delay.TotalMilliseconds, expeditableDelaySource: AsynchronousOperationListenerProvider.NullListener); _resettableDelay = delay; delay.Task.ContinueWith(InvalidateAsync, CancellationToken.None, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); async Task InvalidateAsync(Task _) { var document = _workspace.CurrentSolution.GetDocument(_documentId); if (document == null) { return; } var newVersion = await document.Project.GetDependentVersionAsync(CancellationToken.None).ConfigureAwait(false); if (newVersion == _lastVersion) { return; } // fire and forget. // ignore any exception such as rpc already disposed (disconnected) _lastVersion = newVersion; await _endPoint.InvokeAsync( nameof(IRemoteCodeLensDataPoint.Invalidate), Array.Empty <object>(), CancellationToken.None).ConfigureAwait(false); } }
public Task <bool> IsFullyLoadedAsync(CancellationToken cancellationToken) { if (_workspace.Options.GetOption(ExperimentationOptions.SolutionStatusService_ForceDelay)) { // this is for prototype/mock for people/teams trying partial solution load prototyping // it shouldn't concern anyone outside of that group. // // "experimentationService.IsExperimentEnabled(WellKnownExperimentNames.PartialLoadMode)" check above // make sure this part of code "Service : IWorkspaceStatusService" is not exposed anyone outside of // the partial solution load group. // // for ones that is in the group, one can try lightbulb behavior through internal option page // such as moving around caret on lines where LB should show up. // // it works as below // 1. when this is first called, we return false and start timer to change its status in delayInMS // 2. once delayInMs passed, we clear the delay and return true. // 3. if it is called before the timeout, we reset the timer and return false if (_lastTimeCalled == null) { var delay = _workspace.Options.GetOption(ExperimentationOptions.SolutionStatusService_DelayInMS); _lastTimeCalled = new ResettableDelay(delayInMilliseconds: delay, AsynchronousOperationListenerProvider.NullListener); _ = _lastTimeCalled.Task.SafeContinueWith(_ => StatusChanged?.Invoke(this, true), TaskScheduler.Default); return(SpecializedTasks.False); } if (_lastTimeCalled.Task.IsCompleted) { _lastTimeCalled = null; return(SpecializedTasks.True); } _lastTimeCalled.Reset(); return(SpecializedTasks.False); } // we are using this API for now, until platform provide us new API for prototype return(KnownUIContexts.SolutionExistsAndFullyLoadedContext.IsActive ? SpecializedTasks.True : SpecializedTasks.False); }