/// <summary> /// Tracks obtaining commit characters and committing /// </summary> /// <param name="telemetry">Telemetry from <see cref="IAsyncCompletionSession"/></param> /// <param name="sourceData">Data aggregator</param> private static void AddCommitManagerData(CompletionSessionTelemetry telemetry, Dictionary <string, AggregateCommitManagerData> commitManagerData) { var commitKey = telemetry.CommitManagerName; if (!string.IsNullOrEmpty(commitKey)) { // commitKey is empty when session is dismissed without committing. if (!commitManagerData.ContainsKey(commitKey)) { commitManagerData[commitKey] = new AggregateCommitManagerData(); } var aggregateCommitManagerData = commitManagerData[commitKey]; aggregateCommitManagerData.TotalCommitTime += telemetry.CommitDuration; aggregateCommitManagerData.CommitCount++; aggregateCommitManagerData.MaxCommitTime = Math.Max(aggregateCommitManagerData.MaxCommitTime, telemetry.CommitDuration); } foreach (var commitManagerSetupData in telemetry.CommitManagerSetupDuration) { if (!commitManagerData.ContainsKey(commitManagerSetupData.Key)) { commitManagerData[commitManagerSetupData.Key] = new AggregateCommitManagerData(); } var aggregateCommitManagerData = commitManagerData[commitManagerSetupData.Key]; aggregateCommitManagerData.TotalSetupTime += commitManagerSetupData.Value; aggregateCommitManagerData.SetupCount++; aggregateCommitManagerData.MaxSetupTime = Math.Max(aggregateCommitManagerData.MaxSetupTime, commitManagerSetupData.Value); } }
/// <summary> /// Tracks obtaining applicable span and getting items /// </summary> /// <param name="telemetry">Telemetry from <see cref="IAsyncCompletionSession"/></param> /// <param name="sourceData">Data aggregator</param> private static void AddSourceData(CompletionSessionTelemetry telemetry, Dictionary <string, AggregateSourceData> sourceData) { foreach (var setupData in telemetry.ItemSourceSetupDuration) { if (!sourceData.ContainsKey(setupData.Key)) { sourceData[setupData.Key] = new AggregateSourceData(); } var aggregateSourceData = sourceData[setupData.Key]; aggregateSourceData.TotalSetupTime += setupData.Value; aggregateSourceData.SetupCount++; aggregateSourceData.MaxSetupTime = Math.Max(aggregateSourceData.MaxSetupTime, setupData.Value); } foreach (var getContextData in telemetry.ItemSourceGetContextDuration) { if (!sourceData.ContainsKey(getContextData.Key)) { sourceData[getContextData.Key] = new AggregateSourceData(); } var aggregateSourceData = sourceData[getContextData.Key]; aggregateSourceData.TotalGetContextTime += getContextData.Value; aggregateSourceData.GetContextCount++; } }
#pragma warning restore CA1822 #endregion #region MEF part helper methods private void GetCommitManagersAndChars( SnapshotPoint triggerLocation, Func <IContentType, ITextViewRoleSet, IReadOnlyList <Lazy <IAsyncCompletionCommitManagerProvider, IOrderableContentTypeAndOptionalTextViewRoleMetadata> > > getImports, ITextSnapshot rootSnapshot, ITextView textView, CompletionSessionTelemetry telemetry, out IList <(IAsyncCompletionCommitManager, ITextBuffer)> managersWithBuffers,
public async Task <AggregatedCompletionContext> GetAggregatedCompletionContextAsync(ITextView textView, CompletionTrigger trigger, SnapshotPoint triggerLocation, CancellationToken token) { if (token.IsCancellationRequested || textView.IsClosed) { return(AggregatedCompletionContext.Empty); } var telemetryHost = GetOrCreateTelemetry(textView); var telemetry = new CompletionSessionTelemetry(telemetryHost, headless: true); // ----- GetCompletionSources and GetRootSnapshot need to be run on the UI thread: await JoinableTaskContext.Factory.SwitchToMainThreadAsync(); if (token.IsCancellationRequested || textView.IsClosed) { return(AggregatedCompletionContext.Empty); } var rootSnapshot = GetRootSnapshot(textView); GetCompletionSources(triggerLocation, GetItemSourceProviders, rootSnapshot, textView, textView.BufferGraph, trigger, telemetry, token, out var sourcesWithLocations, out var applicableToSpan); // ----- Go back to background thread to continue processing await TaskScheduler.Default; if (token.IsCancellationRequested || textView.IsClosed) { return(AggregatedCompletionContext.Empty); } // No source declared an appropriate ApplicableToSpan if (applicableToSpan == default) { return(AggregatedCompletionContext.Empty); } // No source wishes to participate if (!sourcesWithLocations.Any()) { return(null); } var aggregatingSession = AsyncCompletionSession.CreateAggregatingSession(applicableToSpan, JoinableTaskContext, sourcesWithLocations, this, textView, telemetry, GuardedOperations); var completionData = await aggregatingSession.ConnectToCompletionSources(trigger, triggerLocation, rootSnapshot, token).ConfigureAwait(true); if (completionData.IsCanceled) { return(AggregatedCompletionContext.Empty); } var aggregateCompletionContext = new CompletionContext( completionData.InitialCompletionItems, completionData.RequestedSuggestionItemOptions, completionData.InitialSelectionHint); return(new AggregatedCompletionContext(aggregateCompletionContext, aggregatingSession)); }
private static void AddBlockingExtensionData(CompletionSessionTelemetry telemetry, Dictionary <string, int> blockingExtensionData) { foreach (var blockingExtension in telemetry.BlockingExtensionCounter) { if (!blockingExtensionData.ContainsKey(blockingExtension.Key)) { blockingExtensionData[blockingExtension.Key] = 0; } blockingExtensionData[blockingExtension.Key] += blockingExtension.Value; } }
/// <summary> /// Adds data from <see cref="CompletionSessionTelemetry" /> to appropriate buckets. /// </summary> /// <param name=""></param> internal void Add(CompletionSessionTelemetry telemetry) { if (_logger == null) { return; } AddSourceData(telemetry, SourceData); AddItemManagerData(telemetry, ItemManagerData); AddCommitManagerData(telemetry, CommitManagerData); AddPresenterData(telemetry, PresenterData); AddE2EData(telemetry, E2EData); }
/// <summary> /// Tracks opening, updating and closing the GUI /// </summary> /// <param name="telemetry">Telemetry from <see cref="IAsyncCompletionSession"/></param> /// <param name="sourceData">Data aggregator</param> private static void AddPresenterData(CompletionSessionTelemetry telemetry, Dictionary <string, AggregatePresenterData> presenterData) { var presenterKey = telemetry.PresenterProviderName; if (!presenterData.ContainsKey(presenterKey)) { presenterData[presenterKey] = new AggregatePresenterData(); } var aggregatePresenterData = presenterData[presenterKey]; aggregatePresenterData.InitialRenderTime += telemetry.InitialRenderingDuration; aggregatePresenterData.TotalRenderTime += telemetry.TotalRenderingDuration; aggregatePresenterData.RenderCount += telemetry.TotalRenderingCount; aggregatePresenterData.TotalClosingTime += telemetry.ClosingDuration; aggregatePresenterData.ClosingCount++; aggregatePresenterData.MaxRenderTime = Math.Max(aggregatePresenterData.MaxRenderTime, telemetry.InitialRenderingDuration); aggregatePresenterData.MaxClosingTime = Math.Max(aggregatePresenterData.MaxClosingTime, telemetry.ClosingDuration); }
/// <summary> /// Tracks sorting and filtering items /// </summary> /// <param name="telemetry">Telemetry from <see cref="IAsyncCompletionSession"/></param> /// <param name="sourceData">Data aggregator</param> private static void AddItemManagerData(CompletionSessionTelemetry telemetry, Dictionary <string, AggregateItemManagerData> itemManagerData) { var itemManagerKey = telemetry.ItemManagerName; if (!itemManagerData.ContainsKey(itemManagerKey)) { itemManagerData[itemManagerKey] = new AggregateItemManagerData(); } var aggregateItemManagerData = itemManagerData[itemManagerKey]; aggregateItemManagerData.InitialProcessTime += telemetry.InitialProcessingDuration; aggregateItemManagerData.TotalProcessTime += telemetry.TotalProcessingDuration; aggregateItemManagerData.TotalBlockingComputationTime += telemetry.BlockingComputationDuration; aggregateItemManagerData.ProcessCount += telemetry.TotalProcessingCount; aggregateItemManagerData.TotalKeystrokes += telemetry.NumberOfKeystrokes; aggregateItemManagerData.UserEverScrolled += telemetry.UserEverScrolled ? 1 : 0; aggregateItemManagerData.UserEverSetFilters += telemetry.UserEverSetFilters ? 1 : 0; aggregateItemManagerData.FinalItemCount += telemetry.FinalItemCount; aggregateItemManagerData.SessionCount++; aggregateItemManagerData.MaxBlockingComputationTime = Math.Max(aggregateItemManagerData.MaxBlockingComputationTime, telemetry.BlockingComputationDuration); }
private static void AddE2EData(CompletionSessionTelemetry telemetry, AggregateE2EData e2eData) { switch (telemetry.CompletionState) { case CompletionSessionState.Committed: e2eData.Committed++; break; case CompletionSessionState.CommittedSuggestionItem: e2eData.CommittedSuggestionItem++; break; case CompletionSessionState.CommittedThroughClick: e2eData.CommittedThroughClick++; break; case CompletionSessionState.CommittedThroughCompleteWord: e2eData.CommittedThroughCompleteWord++; break; case CompletionSessionState.DismissedDueToBackspace: e2eData.DismissedDueToBackspace++; break; case CompletionSessionState.DismissedDueToCancellation: e2eData.DismissedDueToCancellation++; break; case CompletionSessionState.DismissedDueToCaretLeaving: e2eData.DismissedDueToCaretLeaving++; break; case CompletionSessionState.DismissedDuringFiltering: e2eData.DismissedDuringFiltering++; break; case CompletionSessionState.DismissedDueToNonBlockingMode: e2eData.DismissedDueToNonBlockingMode++; break; case CompletionSessionState.DismissedDueToUnhandledError: e2eData.DismissedDueToUnhandledError++; break; case CompletionSessionState.DismissedThroughUI: e2eData.DismissedThroughUI++; break; case CompletionSessionState.DismissedUninitialized: e2eData.DismissedUninitialized++; break; default: e2eData.Dismissed++; break; } if (telemetry.CompletionState == CompletionSessionState.DismissedDueToCancellation) { e2eData.HistogramBucketCanceled++; } else { var E2eDuration = telemetry.E2EStopwatch.ElapsedMilliseconds; if (E2eDuration == 0) { e2eData.HistogramBucketInvalid++; } else if (E2eDuration <= 25) { e2eData.HistogramBucket25++; } else if (E2eDuration <= 50) { e2eData.HistogramBucket50++; } else if (E2eDuration <= 100) { e2eData.HistogramBucket100++; } else if (E2eDuration <= 250) { e2eData.HistogramBucket250++; } else if (E2eDuration <= 500) { e2eData.HistogramBucket500++; } else if (E2eDuration <= 1000) { e2eData.HistogramBucket1000++; } else if (E2eDuration <= 2000) { e2eData.HistogramBucket2000++; } else { e2eData.HistogramBucketLast++; } } }
public IAsyncCompletionSession TriggerCompletion(ITextView textView, CompletionTrigger trigger, SnapshotPoint triggerLocation, CancellationToken token) { var session = GetSession(textView); if (session != null) { return(session); } // This is a simple check that only queries the feature service. // If it succeeds, we will map triggerLocation to available buffers to discover MEF parts. // This is expensive but projected languages require it to discover parts in all available buffers. // To avoid doing this work, call IsCompletionSupported with appropriate IContentType prior to calling TriggerCompletion if (!CompletionAvailability.IsCurrentlyAvailable(textView)) { return(null); } if (textView.IsClosed) { return(null); } if (!JoinableTaskContext.IsOnMainThread) { throw new InvalidOperationException($"This method must be callled on the UI thread."); } var telemetryHost = GetOrCreateTelemetry(textView); var telemetry = new CompletionSessionTelemetry(telemetryHost); var rootSnapshot = GetRootSnapshot(textView); if (token.IsCancellationRequested || textView.IsClosed) { return(null); } // See if we can use more aggressive cancellation token for typing scenarios if (trigger.Reason == CompletionTriggerReason.Insertion) { token = CompletionUtilities.GetResponsiveToken(textView, token); } GetCompletionSources(triggerLocation, GetItemSourceProviders, rootSnapshot, textView, textView.BufferGraph, trigger, telemetry, token, out var sourcesWithLocations, out var applicableToSpan); if (token.IsCancellationRequested || textView.IsClosed) { return(null); } // No source declared an appropriate ApplicableToSpan if (applicableToSpan == default) { return(null); } // No source wishes to participate if (!sourcesWithLocations.Any()) { return(null); } // Some of our extensions need to initialize the source providers before they initialize commit manager providers. // Therefore, it is important to invoke GetCommitManagerProviders after invoking GetItemSourceProviders. GetCommitManagersAndChars(triggerLocation, GetCommitManagerProviders, rootSnapshot, textView, telemetry, out var managersWithBuffers, out var potentialCommitChars); if (_contentTypeComparer == null) { _contentTypeComparer = new StableContentTypeComparer(ContentTypeRegistryService); } var itemManager = GetItemManager(triggerLocation, GetItemManagerProviders, rootSnapshot, textView, _contentTypeComparer); var presenterProvider = GetPresenterProvider(triggerLocation, GetPresenters, rootSnapshot, textView.Roles, _contentTypeComparer); if (token.IsCancellationRequested || textView.IsClosed) { return(null); } session = new AsyncCompletionSession(applicableToSpan, potentialCommitChars, JoinableTaskContext, presenterProvider, sourcesWithLocations, managersWithBuffers, itemManager, this, textView, telemetry, GuardedOperations); textView.Properties.AddProperty(typeof(IAsyncCompletionSession), session); textView.Closed += DismissSessionOnViewClosed; EmulateLegacyCompletionTelemetry(textView); GuardedOperations.RaiseEvent(this, CompletionTriggered, new CompletionTriggeredEventArgs(session, textView)); return(session); }