private Model SetModelExplicitlySelectedItemInBackground( Model model, Func<Model, SignatureHelpItem> selector) { AssertIsBackground(); if (model == null) { return null; } var selectedItem = selector(model); Contract.ThrowIfFalse(model.Items.Contains(selectedItem)); return model.WithSelectedItem(selectedItem); }
private async Task<Model> ComputeModelInBackgroundAsync( Model currentModel, IList<ISignatureHelpProvider> matchedProviders, IList<ISignatureHelpProvider> unmatchedProviders, SnapshotPoint caretPosition, DisconnectedBufferGraph disconnectedBufferGraph, SignatureHelpTriggerInfo triggerInfo, CancellationToken cancellationToken) { try { using (Logger.LogBlock(FunctionId.SignatureHelp_ModelComputation_ComputeModelInBackground, cancellationToken)) { AssertIsBackground(); cancellationToken.ThrowIfCancellationRequested(); var document = await Controller.DocumentProvider.GetDocumentAsync(caretPosition.Snapshot, cancellationToken).ConfigureAwait(false); if (document == null) { return currentModel; } if (triggerInfo.TriggerReason == SignatureHelpTriggerReason.RetriggerCommand) { if (currentModel == null || (triggerInfo.TriggerCharacter.HasValue && !currentModel.Provider.IsRetriggerCharacter(triggerInfo.TriggerCharacter.Value))) { return currentModel; } } // first try to query the providers that can trigger on the specified character var result = await ComputeItemsAsync(matchedProviders, caretPosition, triggerInfo, document, cancellationToken).ConfigureAwait(false); var provider = result.Item1; var items = result.Item2; if (provider == null) { // no match, so now query the other providers result = await ComputeItemsAsync(unmatchedProviders, caretPosition, triggerInfo, document, cancellationToken).ConfigureAwait(false); provider = result.Item1; items = result.Item2; if (provider == null) { // the other providers didn't produce items either, so we don't produce a model return null; } } if (currentModel != null && currentModel.Provider == provider && currentModel.GetCurrentSpanInSubjectBuffer(disconnectedBufferGraph.SubjectBufferSnapshot).Span.Start == items.ApplicableSpan.Start && currentModel.ArgumentIndex == items.ArgumentIndex && currentModel.ArgumentCount == items.ArgumentCount && currentModel.ArgumentName == items.ArgumentName) { // The new model is the same as the current model. Return the currentModel // so we keep the active selection. return currentModel; } var selectedItem = GetSelectedItem(currentModel, items, provider); var model = new Model(disconnectedBufferGraph, items.ApplicableSpan, provider, items.Items, selectedItem, items.ArgumentIndex, items.ArgumentCount, items.ArgumentName, selectedParameter: 0); var syntaxFactsService = document.GetLanguageService<ISyntaxFactsService>(); var isCaseSensitive = syntaxFactsService == null || syntaxFactsService.IsCaseSensitive; var selection = DefaultSignatureHelpSelector.GetSelection(model.Items, model.SelectedItem, model.ArgumentIndex, model.ArgumentCount, model.ArgumentName, isCaseSensitive); return model.WithSelectedItem(selection.SelectedItem) .WithSelectedParameter(selection.SelectedParameter); } } catch (Exception e) when (FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
private static SignatureHelpItem GetSelectedItem(Model currentModel, SignatureHelpItems items, ISignatureHelpProvider provider) { // Try to find the most appropriate item in the list to select by default. // If the provider specified one a selected item, then always stick with that one. if (items.SelectedItemIndex.HasValue) { return items.Items[items.SelectedItemIndex.Value]; } // If the provider did not pick a default, and it's the same provider as the previous // model we have, then try to return the same item that we had before. if (currentModel != null && currentModel.Provider == provider) { return items.Items.FirstOrDefault(i => DisplayPartsMatch(i, currentModel.SelectedItem)) ?? items.Items.First(); } // Otherwise, just pick the first item we have. return items.Items.First(); }