protected override async Task<IEnumerable<CompletionItem>> GetItemsWorkerAsync(Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { return await document.GetUnionResultsFromDocumentAndLinks( UnionCompletionItemComparer.Instance, async (doc, ct) => await GetSpeculativeTCompletions(doc, position, ct).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); }
private Model( DisconnectedBufferGraph disconnectedBufferGraph, IList<CompletionItem> totalItems, IList<CompletionItem> filteredItems, CompletionItem selectedItem, ImmutableArray<CompletionItemFilter> completionItemFilters, ImmutableDictionary<CompletionItemFilter, bool> filterState, IReadOnlyDictionary<CompletionItem, string> completionItemToFilterText, bool isHardSelection, bool isUnique, bool useSuggestionCompletionMode, CompletionItem builder, CompletionItem defaultBuilder, CompletionTriggerInfo triggerInfo, ITrackingPoint commitSpanEndPoint, bool dismissIfEmpty) { Contract.ThrowIfFalse(totalItems.Count != 0, "Must have at least one item."); _disconnectedBufferGraph = disconnectedBufferGraph; this.TotalItems = totalItems; this.FilteredItems = filteredItems; this.FilterState = filterState; this.SelectedItem = selectedItem; this.CompletionItemFilters = completionItemFilters; this.CompletionItemToFilterText = completionItemToFilterText; this.IsHardSelection = isHardSelection; this.IsUnique = isUnique; this.UseSuggestionCompletionMode = useSuggestionCompletionMode; this.Builder = builder; this.DefaultBuilder = defaultBuilder; this.TriggerInfo = triggerInfo; this.CommitTrackingSpanEndPoint = commitSpanEndPoint; this.DismissIfEmpty = dismissIfEmpty; }
protected override async Task<IEnumerable<CompletionItem>> GetItemsWorkerAsync(Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (tree.IsInNonUserCode(position, cancellationToken)) { return null; } var targetToken = tree.FindTokenOnLeftOfPosition(position, cancellationToken).GetPreviousTokenIfTouchingWord(position); if (targetToken.IsKind(SyntaxKind.AliasKeyword) && targetToken.Parent.IsKind(SyntaxKind.ExternAliasDirective)) { var compilation = await document.GetCSharpCompilationAsync(cancellationToken).ConfigureAwait(false); var aliases = compilation.ExternalReferences.SelectMany(r => r.Properties.Aliases).ToSet(); if (aliases.Any()) { var root = await tree.GetRootAsync(cancellationToken).ConfigureAwait(false); var usedAliases = root.ChildNodes().OfType<ExternAliasDirectiveSyntax>().Where(e => !e.Identifier.IsMissing).Select(e => e.Identifier.ValueText); aliases.RemoveRange(usedAliases); aliases.Remove(MetadataReferenceProperties.GlobalAlias); var textChangeSpan = CompletionUtilities.GetTextChangeSpan(await document.GetTextAsync(cancellationToken).ConfigureAwait(false), position); return aliases.Select(e => new CompletionItem(this, e, textChangeSpan, glyph: Glyph.Namespace)); } } return SpecializedCollections.EmptyEnumerable<CompletionItem>(); }
public bool?MatchesFilterText(CompletionItem item, string filterText, CompletionTriggerInfo triggerInfo, CompletionFilterReason filterReason) { // If the user hasn't typed anything, and this item was preselected, or was in the // MRU list, then we definitely want to include it. if (filterText.Length == 0) { if (item.Preselect || _completionService.GetMRUIndex(item) < 0) { return(true); } } if (IsAllDigits(filterText)) { // The user is just typing a number. We never want this to match against // anything we would put in a completion list. return(false); } var match = _patternMatcher.MatchPatternFirstOrNullable( _completionService.GetCultureSpecificQuirks(item.FilterText), _completionService.GetCultureSpecificQuirks(filterText)); return(match != null); }
private Model( DisconnectedBufferGraph disconnectedBufferGraph, IList<CompletionItem> totalItems, IList<CompletionItem> filteredItems, CompletionItem selectedItem, bool isHardSelection, bool isUnique, bool useSuggestionCompletionMode, CompletionItem builder, CompletionItem defaultBuilder, CompletionTriggerInfo triggerInfo, ITrackingPoint commitSpanEndPoint, bool dismissIfEmpty) { Contract.ThrowIfNull(selectedItem); Contract.ThrowIfFalse(totalItems.Count != 0, "Must have at least one item."); Contract.ThrowIfFalse(filteredItems.Count != 0, "Must have at least one filtered item."); Contract.ThrowIfFalse(filteredItems.Contains(selectedItem) || defaultBuilder == selectedItem, "Selected item must be in filtered items."); _disconnectedBufferGraph = disconnectedBufferGraph; this.TotalItems = totalItems; this.FilteredItems = filteredItems; this.SelectedItem = selectedItem; this.IsHardSelection = isHardSelection; this.IsUnique = isUnique; this.UseSuggestionCompletionMode = useSuggestionCompletionMode; this.Builder = builder; this.DefaultBuilder = defaultBuilder; this.TriggerInfo = triggerInfo; this.CommitTrackingSpanEndPoint = commitSpanEndPoint; this.DismissIfEmpty = dismissIfEmpty; }
private static async Task <CompletionList> GetCompletionListAsync( CompletionListProvider provider, Document documentOpt, SourceText text, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { if (provider is TextCompletionProvider) { return(((TextCompletionProvider)provider).GetCompletionList(text, position, triggerInfo, cancellationToken)); } if (documentOpt != null) { var context = new CompletionListContext(documentOpt, position, triggerInfo, cancellationToken); await provider.ProduceCompletionListAsync(context).ConfigureAwait(false); return(new CompletionList(context.GetItems(), context.Builder, context.IsExclusive)); } Contract.Fail("Should never get here."); return(null); }
/// <summary> /// Returns the <see cref="CompletionList"/> for the specified <paramref name="position"/> /// in the <paramref name="document"/>. /// </summary> public static Task<CompletionList> GetCompletionListAsync( Document document, int position, CompletionTriggerInfo triggerInfo, OptionSet options = null, IEnumerable<CompletionListProvider> providers = null, CancellationToken cancellationToken = default(CancellationToken)) { if (document == null) { throw new ArgumentNullException(nameof(document)); } var completionService = document.GetLanguageService<ICompletionService>(); if (completionService != null) { options = options ?? document.Project.Solution.Workspace.Options; providers = providers ?? GetDefaultCompletionListProviders(document); return completionService.GetCompletionListAsync(document, position, triggerInfo, options, providers, cancellationToken); } else { if (s_emptyCompletionListTask == null) { var value = Task.FromResult(new CompletionList(ImmutableArray<CompletionItem>.Empty)); Interlocked.CompareExchange(ref s_emptyCompletionListTask, value, null); } return s_emptyCompletionListTask; } }
protected override async Task<bool> IsExclusiveAsync(Document document, int caretPosition, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var token = syntaxTree.FindTokenOnLeftOfPosition(caretPosition, cancellationToken) .GetPreviousTokenIfTouchingWord(caretPosition); return IsAfterNameColonArgument(token) || IsAfterNameEqualsArgument(token); }
protected override async Task<IEnumerable<CompletionItem>> GetItemsWorkerAsync(Document document, int position, CompletionTriggerInfo triggerInfo, System.Threading.CancellationToken cancellationToken) { var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (!tree.IsEntirelyWithinCrefSyntax(position, cancellationToken)) { return null; } var token = tree.FindTokenOnLeftOfPosition(position, cancellationToken); token = token.GetPreviousTokenIfTouchingWord(position); if (token.Kind() == SyntaxKind.None) { return null; } var result = SpecializedCollections.EmptyEnumerable<ISymbol>(); // To get a Speculative SemanticModel (which is much faster), we need to // walk up to the node the DocumentationTrivia is attached to. var parentNode = token.GetAncestor<DocumentationCommentTriviaSyntax>().ParentTrivia.Token.Parent; var semanticModel = await document.GetSemanticModelForNodeAsync(parentNode, cancellationToken).ConfigureAwait(false); // cref ""|, ""|"", ""a|"" if (token.IsKind(SyntaxKind.DoubleQuoteToken, SyntaxKind.SingleQuoteToken) && token.Parent.IsKind(SyntaxKind.XmlCrefAttribute)) { result = semanticModel.LookupSymbols(token.SpanStart) .FilterToVisibleAndBrowsableSymbols(document.ShouldHideAdvancedMembers(), semanticModel.Compilation); result = result.Concat(GetOperatorsAndIndexers(token, semanticModel, cancellationToken)); } else if (IsSignatureContext(token)) { result = semanticModel.LookupNamespacesAndTypes(token.SpanStart) .FilterToVisibleAndBrowsableSymbols(document.ShouldHideAdvancedMembers(), semanticModel.Compilation); } else if (token.IsKind(SyntaxKind.DotToken) && token.Parent.IsKind(SyntaxKind.QualifiedCref)) { // cref "a.|" var parent = token.Parent as QualifiedCrefSyntax; var leftType = semanticModel.GetTypeInfo(parent.Container, cancellationToken).Type; var leftSymbol = semanticModel.GetSymbolInfo(parent.Container, cancellationToken).Symbol; var container = leftSymbol ?? leftType; result = semanticModel.LookupSymbols(token.SpanStart, container: (INamespaceOrTypeSymbol)container) .FilterToVisibleAndBrowsableSymbols(document.ShouldHideAdvancedMembers(), semanticModel.Compilation); if (container is INamedTypeSymbol) { result = result.Concat(((INamedTypeSymbol)container).InstanceConstructors); } } return await CreateItemsAsync(document.Project.Solution.Workspace, semanticModel, position, result, token, cancellationToken).ConfigureAwait(false); }
public Task <IEnumerable <CompletionItemGroup> > GetGroupsAsync( SourceText text, int position, CompletionTriggerInfo triggerInfo, IEnumerable <ICompletionProvider> completionProviders, OptionSet options, CancellationToken cancellationToken) { return(GetGroupsAsync(null, text, position, triggerInfo, completionProviders, options, cancellationToken)); }
public async Task<IEnumerable<CompletionItemGroup>> GetGroupsAsync( Document document, int position, CompletionTriggerInfo triggerInfo, IEnumerable<ICompletionProvider> completionProviders, CancellationToken cancellationToken) { var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); return await GetGroupsAsync(document, text, position, triggerInfo, completionProviders, document.Project.Solution.Workspace.Options, cancellationToken).ConfigureAwait(false); }
public override CompletionList GetCompletionList(SourceText text, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken = default(CancellationToken)) { var items = this.GetItems(text, position, triggerInfo, cancellationToken); if (items == null || !items.Any()) { return null; } return new CompletionList(items); }
public async Task <IEnumerable <CompletionItemGroup> > GetGroupsAsync( Document document, int position, CompletionTriggerInfo triggerInfo, IEnumerable <ICompletionProvider> completionProviders, CancellationToken cancellationToken) { var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); return(await GetGroupsAsync(document, text, position, triggerInfo, completionProviders, document.Project.Solution.Workspace.Options, cancellationToken).ConfigureAwait(false)); }
private static async Task <CompletionList> GetCompletionListAsync( CompletionListProvider provider, Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { var context = new CompletionListContext(document, position, triggerInfo, cancellationToken); await provider.ProduceCompletionListAsync(context).ConfigureAwait(false); return(new CompletionList(context.GetItems(), context.Builder, context.IsExclusive)); }
protected override async Task<IEnumerable<CompletionItem>> GetItemsWorkerAsync(Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); DeclarationModifiers modifiers; SyntaxToken token; if (!IsPartialCompletionContext(tree, position, cancellationToken, out modifiers, out token)) { return null; } return await CreatePartialItemsAsync(document, position, modifiers, token, cancellationToken).ConfigureAwait(false); }
protected override async Task<IEnumerable<CompletionItem>> GetItemsWorkerAsync(Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); // first try to get the #r string literal token. If we couldn't, then we're not in a #r // reference directive and we immediately bail. SyntaxToken stringLiteral; if (!TryGetStringLiteralToken(tree, position, out stringLiteral, cancellationToken)) { return null; } var documentPath = document.Project.IsSubmission ? null : document.FilePath; var textChangeSpan = this.GetTextChangeSpan(stringLiteral, position); var gacHelper = new GlobalAssemblyCacheCompletionHelper(this, textChangeSpan, itemRules: ItemRules.Instance); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var snapshot = text.FindCorrespondingEditorTextSnapshot(); if (snapshot == null) { // Passing null to GetFileSystemDiscoveryService raises an exception. // Instead, return here since there is no longer snapshot for this document. return null; } var assemblyReferenceResolver = document.Project.CompilationOptions.MetadataReferenceResolver as AssemblyReferenceResolver; if (assemblyReferenceResolver == null) { return null; } var metadataFileResolver = assemblyReferenceResolver.PathResolver as MetadataFileReferenceResolver; if (metadataFileResolver == null) { return null; } var fileSystemHelper = new FileSystemCompletionHelper( this, textChangeSpan, GetFileSystemDiscoveryService(snapshot), Glyph.OpenFolder, Glyph.Assembly, searchPaths: metadataFileResolver.SearchPaths, allowableExtensions: new[] { ".dll", ".exe" }, exclude: path => path.Contains(","), itemRules: ItemRules.Instance); var pathThroughLastSlash = this.GetPathThroughLastSlash(stringLiteral, position); return gacHelper.GetItems(pathThroughLastSlash, documentPath).Concat( fileSystemHelper.GetItems(pathThroughLastSlash, documentPath)); }
private static Task <CompletionItemGroup> GetGroupAsync( ICompletionProvider provider, Document documentOpt, SourceText text, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { return(provider is ITextCompletionProvider ? Task.FromResult(((ITextCompletionProvider)provider).GetGroup(text, position, triggerInfo, cancellationToken)) : documentOpt != null ? provider.GetGroupAsync(documentOpt, position, triggerInfo, cancellationToken) : SpecializedTasks.Default <CompletionItemGroup>()); }
protected override async Task<CompletionItem> GetBuilderAsync(Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken = default(CancellationToken)) { if (triggerInfo.TriggerReason == CompletionTriggerReason.TypeCharCommand) { var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); if (triggerInfo.IsDebugger) { // Aggressive Intellisense in the debugger: always show the builder return CreateEmptyBuilder(text, position); } var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var token = tree .FindTokenOnLeftOfPosition(position, cancellationToken) .GetPreviousTokenIfTouchingWord(position); if (token.Kind() == SyntaxKind.None) { return null; } var semanticModel = await document.GetSemanticModelForNodeAsync(token.Parent, cancellationToken).ConfigureAwait(false); var typeInferrer = document.GetLanguageService<ITypeInferenceService>(); if (IsLambdaExpression(semanticModel, position, token, typeInferrer, cancellationToken)) { return CreateBuilder(text, position, CSharpFeaturesResources.LambdaExpression, CSharpFeaturesResources.AutoselectDisabledDueToPotentialLambdaDeclaration); } else if (IsAnonymousObjectCreation(token)) { return CreateBuilder(text, position, CSharpFeaturesResources.MemberName, CSharpFeaturesResources.AutoselectDisabledDueToPossibleExplicitlyNamesAnonTypeMemCreation); } else if (token.IsPreProcessorExpressionContext()) { return CreateEmptyBuilder(text, position); } else if (IsImplicitArrayCreation(semanticModel, token, position, typeInferrer, cancellationToken)) { return CreateBuilder(text, position, CSharpFeaturesResources.ImplicitArrayCreation, CSharpFeaturesResources.AutoselectDisabledDueToPotentialImplicitArray); } else if (token.IsKindOrHasMatchingText(SyntaxKind.FromKeyword) || token.IsKindOrHasMatchingText(SyntaxKind.JoinKeyword)) { return CreateBuilder(text, position, CSharpFeaturesResources.RangeVariable, CSharpFeaturesResources.AutoselectDisabledDueToPotentialRangeVariableDecl); } } return null; }
public bool? IsBetterFilterMatch(CompletionItem item1, CompletionItem item2, string filterText, CompletionTriggerInfo triggerInfo, CompletionFilterReason filterReason) { var match1 = _patternMatcher.MatchPatternFirstOrNullable( _completionService.GetCultureSpecificQuirks(item1.FilterText), _completionService.GetCultureSpecificQuirks(filterText)); var match2 = _patternMatcher.MatchPatternFirstOrNullable( _completionService.GetCultureSpecificQuirks(item2.FilterText), _completionService.GetCultureSpecificQuirks(filterText)); if (match1 != null && match2 != null) { var result = match1.Value.CompareTo(match2.Value); if (result != 0) { return result < 0; } } else if (match1 != null) { return true; } else if (match2 != null) { return false; } // If they both seemed just as good, but they differ on preselection, then // item1 is better if it is preselected, otherwise it it worse. if (item1.Preselect != item2.Preselect) { return item1.Preselect; } // Prefer things with a keyword glyph, if the filter texts are the same. if (item1.Glyph != item2.Glyph && item1.FilterText == item2.FilterText) { return item1.Glyph == Glyph.Keyword; } // They matched on everything, including preselection values. Item1 is better if it // has a lower MRU index. var item1MRUIndex = _completionService.GetMRUIndex(item1); var item2MRUIndex = _completionService.GetMRUIndex(item2); // The one with the lower index is the better one. return item1MRUIndex < item2MRUIndex; }
private ImmutableArray<CompletionItem> GetItems(SourceText text, Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { var line = text.Lines.GetLineFromPosition(position); var lineText = text.ToString(TextSpan.FromBounds(line.Start, position)); var match = s_directiveRegex.Match(lineText); if (!match.Success) { return ImmutableArray<CompletionItem>.Empty; } var quotedPathGroup = match.Groups[1]; var quotedPath = quotedPathGroup.Value; var endsWithQuote = PathCompletionUtilities.EndsWithQuote(quotedPath); if (endsWithQuote && (position >= line.Start + match.Length)) { return ImmutableArray<CompletionItem>.Empty; } var buffer = text.Container.GetTextBuffer(); var snapshot = text.FindCorrespondingEditorTextSnapshot(); if (snapshot == null) { return ImmutableArray<CompletionItem>.Empty; } var fileSystem = CurrentWorkingDirectoryDiscoveryService.GetService(snapshot); // TODO: https://github.com/dotnet/roslyn/issues/5263 // Avoid dependency on a specific resolver. // The search paths should be provided by specialized workspaces: // - InteractiveWorkspace for interactive window // - ScriptWorkspace for loose .csx files (we don't have such workspace today) var searchPaths = (document.Project.CompilationOptions.SourceReferenceResolver as SourceFileResolver)?.SearchPaths ?? ImmutableArray<string>.Empty; var helper = new FileSystemCompletionHelper( this, GetTextChangeSpan(text, position, quotedPathGroup), fileSystem, Glyph.OpenFolder, Glyph.CSharpFile, searchPaths: searchPaths, allowableExtensions: new[] { ".csx" }, itemRules: ItemRules.Instance); var pathThroughLastSlash = this.GetPathThroughLastSlash(text, position, quotedPathGroup); return helper.GetItems(pathThroughLastSlash, documentPath: null); }
public void ComputeModel( ICompletionService completionService, CompletionTriggerInfo triggerInfo, IEnumerable<ICompletionProvider> completionProviders, bool isDebugger) { AssertIsForeground(); // If we've already computed a model then we can just ignore this request and not // generate any tasks. if (this.Computation.InitialUnfilteredModel != null) { return; } new ModelComputer(this, completionService, triggerInfo, completionProviders, isDebugger).Do(); }
public CompletionListContext( Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { if (document == null) { throw new ArgumentNullException(nameof(document)); } this.Document = document; this.Position = position; this.TriggerInfo = triggerInfo; this.CancellationToken = cancellationToken; this._itemsBuilder = ImmutableArray.CreateBuilder<CompletionItem>(); }
public CompletionListContext( Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { if (document == null) { throw new ArgumentNullException(nameof(document)); } this.Document = document; this.Position = position; this.TriggerInfo = triggerInfo; this.CancellationToken = cancellationToken; this._itemsBuilder = ImmutableArray.CreateBuilder <CompletionItem>(); }
public async Task<CompletionItemGroup> GetGroupAsync(Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { if (!triggerInfo.IsAugment) { return null; } var builder = await this.GetBuilderAsync(document, position, triggerInfo, cancellationToken).ConfigureAwait(false); if (builder == null) { return null; } return new CompletionItemGroup( SpecializedCollections.EmptyEnumerable<CompletionItem>(), builder, isExclusive: false); }
private ImmutableArray<CompletionItem> GetItems(SourceText text, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { var line = text.Lines.GetLineFromPosition(position); var lineText = text.ToString(TextSpan.FromBounds(line.Start, position)); var match = s_directiveRegex.Match(lineText); if (!match.Success) { return ImmutableArray<CompletionItem>.Empty; } var quotedPathGroup = match.Groups[1]; var quotedPath = quotedPathGroup.Value; var endsWithQuote = PathCompletionUtilities.EndsWithQuote(quotedPath); if (endsWithQuote && (position >= line.Start + match.Length)) { return ImmutableArray<CompletionItem>.Empty; } var buffer = text.Container.GetTextBuffer(); var snapshot = text.FindCorrespondingEditorTextSnapshot(); if (snapshot == null) { return ImmutableArray<CompletionItem>.Empty; } var fileSystem = PathCompletionUtilities.GetCurrentWorkingDirectoryDiscoveryService(snapshot); var searchPaths = ImmutableArray.Create<string>(fileSystem.CurrentDirectory); var helper = new FileSystemCompletionHelper( this, GetTextChangeSpan(text, position, quotedPathGroup), fileSystem, Glyph.OpenFolder, Glyph.CSharpFile, searchPaths: searchPaths, allowableExtensions: new[] { ".csx" }, itemRules: ItemRules.Instance); var pathThroughLastSlash = this.GetPathThroughLastSlash(text, position, quotedPathGroup); return helper.GetItems(pathThroughLastSlash, documentPath: null); }
protected override async Task<IEnumerable<CompletionItem>> GetItemsWorkerAsync( Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { if (document != null && document.SourceCodeKind == SourceCodeKind.Interactive) { // the provider might be invoked in non-interactive context: Workspace ws; if (Workspace.TryGetWorkspace(document.GetTextAsync(cancellationToken).WaitAndGetResult(cancellationToken).Container, out ws)) { var workspace = ws as InteractiveWorkspace; if (workspace != null) { var window = workspace.Engine.CurrentWindow; var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (tree.IsBeforeFirstToken(position, cancellationToken) && tree.IsPreProcessorKeywordContext(position, cancellationToken)) { var textChangeSpan = await this.GetTextChangeSpanAsync(document, position, cancellationToken).ConfigureAwait(false); var list = new List<CompletionItem>(); IInteractiveWindowCommands commands = window.GetInteractiveCommands(); if (commands != null) { foreach (var command in commands.GetCommands()) { foreach (var commandName in command.Names) { list.Add(new CSharpCompletionItem( workspace, this, commandName, textChangeSpan, c => Task.FromResult(command.Description.ToSymbolDisplayParts()), glyph: Glyph.Intrinsic)); } } } return list; } } } } return SpecializedCollections.EmptyEnumerable<CompletionItem>(); }
public ModelComputer( Session session, ICompletionService completionService, CompletionTriggerInfo triggerInfo, IEnumerable<ICompletionProvider> completionProviders, bool isDebugger) { _session = session; _completionService = completionService; _options = session.Controller.SubjectBuffer.TryGetOptions(); _triggerInfo = triggerInfo; _subjectBufferCaretPosition = session.Controller.TextView.GetCaretPoint(session.Controller.SubjectBuffer).Value; _completionProviders = completionProviders; _text = _subjectBufferCaretPosition.Snapshot.AsText(); _includeBuilder = session.Controller.SubjectBuffer.GetOption(Options.EditorCompletionOptions.UseSuggestionMode); _disconnectedBufferGraph = new DisconnectedBufferGraph(session.Controller.SubjectBuffer, session.Controller.TextView.TextBuffer); }
protected override async Task<IEnumerable<CompletionItem>> GetItemsWorkerAsync(Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { using (Logger.LogBlock(FunctionId.Completion_SnippetCompletionProvider_GetItemsWorker_CSharp, cancellationToken)) { var workspace = document.Project.Solution.Workspace; if (!workspace.CanApplyChange(ApplyChangesKind.ChangeDocument) || workspace.Kind == WorkspaceKind.Debugger) { return SpecializedCollections.EmptyEnumerable<CompletionItem>(); } var optionService = workspace.Services.GetService<IOptionService>(); if (!optionService.GetOption(CSharpCompletionOptions.IncludeSnippets)) { return SpecializedCollections.EmptyEnumerable<CompletionItem>(); } return await document.GetUnionResultsFromDocumentAndLinks(UnionCompletionItemComparer.Instance, async (d, c) => await GetSnippetsForDocumentAsync(d, position, workspace, c).ConfigureAwait(false), cancellationToken).ConfigureAwait(false); } }
/// <summary> /// Returns true if the completion item matches the filter text typed so far. Returns 'true' /// iff the completion item matches and should be included in the filtered completion /// results, or false if it should not be. /// </summary> public virtual bool MatchesFilterText(CompletionItem item, string filterText, CompletionTriggerInfo triggerInfo, CompletionFilterReason filterReason) { // If the user hasn't typed anything, and this item was preselected, or was in the // MRU list, then we definitely want to include it. if (filterText.Length == 0) { if (item.Preselect || _completionService.GetMRUIndex(item) < 0) { return true; } } if (IsAllDigits(filterText)) { // The user is just typing a number. We never want this to match against // anything we would put in a completion list. return false; } return GetMatch(item, filterText) != null; }
private static async Task <CompletionList> GetCompletionListAsync( CompletionListProvider provider, Document document, int position, CompletionTriggerInfo triggerInfo, OptionSet options, CancellationToken cancellationToken) { var context = new CompletionListContext(document, position, triggerInfo, options, cancellationToken); if (document.SupportsSyntaxTree) { var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); if (!root.FullSpan.IntersectsWith(position)) { try { // Trying to track down source of https://github.com/dotnet/roslyn/issues/9325 var sourceText = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); ReportException(position, root, sourceText); } catch (Exception e) when(FatalError.ReportWithoutCrash(e)) { } } else { await provider.ProduceCompletionListAsync(context).ConfigureAwait(false); } } else { await provider.ProduceCompletionListAsync(context).ConfigureAwait(false); } return(new CompletionList(context.GetItems(), context.Builder, context.IsExclusive)); }
public bool? MatchesFilterText(CompletionItem item, string filterText, CompletionTriggerInfo triggerInfo, CompletionFilterReason filterReason) { // If the user hasn't typed anything, and this item was preselected, or was in the // MRU list, then we definitely want to include it. if (filterText.Length == 0) { if (item.Preselect || _completionService.GetMRUIndex(item) < 0) { return true; } } if (IsAllDigits(filterText)) { // The user is just typing a number. We never want this to match against // anything we would put in a completion list. return false; } var match = _patternMatcher.MatchPatternFirstOrNullable( _completionService.GetCultureSpecificQuirks(item.FilterText), _completionService.GetCultureSpecificQuirks(filterText)); return match != null; }
protected override async Task<IEnumerable<CompletionItem>> GetItemsWorkerAsync( Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { var span = new TextSpan(position, 0); var semanticModel = await document.GetSemanticModelForSpanAsync(span, cancellationToken).ConfigureAwait(false); var syntaxTree = semanticModel.SyntaxTree; var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>(); var semanticFacts = document.GetLanguageService<ISemanticFactsService>(); if (syntaxFacts.IsInNonUserCode(syntaxTree, position, cancellationToken) || semanticFacts.IsPreProcessorDirectiveContext(semanticModel, position, cancellationToken)) { return SpecializedCollections.EmptyEnumerable<CompletionItem>(); } if (!syntaxTree.IsRightOfDotOrArrowOrColonColon(position, cancellationToken)) { return SpecializedCollections.EmptyEnumerable<CompletionItem>(); } var node = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken) .GetPreviousTokenIfTouchingWord(position) .Parent; if (node.Kind() == SyntaxKind.ExplicitInterfaceSpecifier) { return await GetCompletionsOffOfExplicitInterfaceAsync( document, semanticModel, position, ((ExplicitInterfaceSpecifierSyntax)node).Name, cancellationToken).ConfigureAwait(false); } return SpecializedCollections.EmptyEnumerable<CompletionItem>(); }
public virtual bool? ShouldSoftSelectItem(CompletionItem item, string filterText, CompletionTriggerInfo triggerInfo) { return filterText.Length == 0 && !item.Preselect; }
/// <summary> /// Returns the <see cref="CompletionList"/> for the specified position in the document. /// </summary> public static Task <CompletionList> GetCompletionListAsync(Document document, int position, CompletionTriggerInfo triggerInfo, IEnumerable <CompletionListProvider> completionProviders = null, CancellationToken cancellationToken = default(CancellationToken)) { return(document.GetLanguageService <ICompletionService>().GetCompletionListAsync(document, position, triggerInfo, completionProviders, cancellationToken)); }
protected override async Task<IEnumerable<CompletionItem>> GetItemsWorkerAsync( Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (syntaxTree.IsInNonUserCode(position, cancellationToken)) { return null; } var token = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken); token = token.GetPreviousTokenIfTouchingWord(position); if (token.Kind() != SyntaxKind.OpenParenToken && token.Kind() != SyntaxKind.CommaToken) { return null; } var attributeArgumentList = token.Parent as AttributeArgumentListSyntax; var attributeSyntax = token.Parent.Parent as AttributeSyntax; if (attributeSyntax == null || attributeArgumentList == null) { return null; } // We actually want to collect two sets of named parameters to present the user. The // normal named parameters that come from the attribute constructors. These will be // presented like "foo:". And also the named parameters that come from the writable // fields/properties in the attribute. These will be presented like "bar =". var existingNamedParameters = GetExistingNamedParameters(attributeArgumentList, position); var workspace = document.Project.Solution.Workspace; var semanticModel = await document.GetSemanticModelForNodeAsync(attributeSyntax, cancellationToken).ConfigureAwait(false); var nameColonItems = await GetNameColonItemsAsync(workspace, semanticModel, position, token, attributeSyntax, existingNamedParameters, cancellationToken).ConfigureAwait(false); var nameEqualsItems = await GetNameEqualsItemsAsync(workspace, semanticModel, position, token, attributeSyntax, existingNamedParameters, cancellationToken).ConfigureAwait(false); // If we're after a name= parameter, then we only want to show name= parameters. if (IsAfterNameEqualsArgument(token)) { return nameEqualsItems; } return nameColonItems.Concat(nameEqualsItems); }
/// <summary> /// Returns true if the completion item matches the filter text typed so far. Returns 'true' /// iff the completion item matches and should be included in the filtered completion /// results, or false if it should not be. /// </summary> public virtual bool MatchesFilterText(CompletionItem item, string filterText, CompletionTriggerInfo triggerInfo, CompletionFilterReason filterReason) { // If the user hasn't typed anything, and this item was preselected, or was in the // MRU list, then we definitely want to include it. if (filterText.Length == 0) { if (item.Preselect || _completionService.GetMRUIndex(item) < 0) { return(true); } } if (IsAllDigits(filterText)) { // The user is just typing a number. We never want this to match against // anything we would put in a completion list. return(false); } return(GetMatch(item, filterText) != null); }
protected override async Task<bool> IsExclusiveAsync(Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { // We're exclusive if this context could only be an object initializer and not also a // collection initializer. If we're initializing something that could be initialized as // an object or as a collection, say we're not exclusive. That way the rest of // intellisense can be used in the collection intializer. // // Consider this case: // class c : IEnumerable<int> // { // public void Add(int addend) { } // public int foo; // } // void foo() // { // var b = new c {| // } // There we could initialize b using either an object initializer or a collection // initializer. Since we don't know which the user will use, we'll be non-exclusive, so // the other providers can help the user write the collection initializer, if they want // to. var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (tree.IsInNonUserCode(position, cancellationToken)) { return false; } var token = tree.FindTokenOnLeftOfPosition(position, cancellationToken); token = token.GetPreviousTokenIfTouchingWord(position); if (token.Parent == null) { return false; } var expression = token.Parent.Parent as ExpressionSyntax; if (expression == null) { return false; } var semanticModel = await document.GetSemanticModelForNodeAsync(expression, cancellationToken).ConfigureAwait(false); var initializedType = semanticModel.GetTypeInfo(expression, cancellationToken).Type; if (initializedType == null) { return false; } // Non-exclusive if initializedType can be initialized as a collection. if (initializedType.CanSupportCollectionInitializer()) { return false; } // By default, only our member names will show up. return true; }
protected override async Task<IEnumerable<CompletionItem>> GetItemsWorkerAsync(Document document, int position, CompletionTriggerInfo triggerInfo, CancellationToken cancellationToken) { var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var token = tree.FindTokenOnLeftOfPosition(position, cancellationToken); var parentTrivia = token.GetAncestor<DocumentationCommentTriviaSyntax>(); if (parentTrivia == null) { return null; } var items = new List<CompletionItem>(); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var span = CompletionUtilities.GetTextChangeSpan(text, position); var attachedToken = parentTrivia.ParentTrivia.Token; if (attachedToken.Kind() == SyntaxKind.None) { return null; } var semanticModel = await document.GetSemanticModelForNodeAsync(attachedToken.Parent, cancellationToken).ConfigureAwait(false); ISymbol declaredSymbol = null; var memberDeclaration = attachedToken.GetAncestor<MemberDeclarationSyntax>(); if (memberDeclaration != null) { declaredSymbol = semanticModel.GetDeclaredSymbol(memberDeclaration, cancellationToken); } else { var typeDeclaration = attachedToken.GetAncestor<TypeDeclarationSyntax>(); if (typeDeclaration != null) { declaredSymbol = semanticModel.GetDeclaredSymbol(typeDeclaration, cancellationToken); } } if (declaredSymbol != null) { items.AddRange(GetTagsForSymbol(declaredSymbol, span, parentTrivia, token)); } if (token.Parent.Kind() == SyntaxKind.XmlEmptyElement || token.Parent.Kind() == SyntaxKind.XmlText || (token.Parent.IsKind(SyntaxKind.XmlElementEndTag) && token.IsKind(SyntaxKind.GreaterThanToken)) || (token.Parent.IsKind(SyntaxKind.XmlName) && token.Parent.IsParentKind(SyntaxKind.XmlEmptyElement))) { if (token.Parent.Parent.Kind() == SyntaxKind.XmlElement) { items.AddRange(GetNestedTags(span)); } if (token.Parent.Parent.Kind() == SyntaxKind.XmlElement && ((XmlElementSyntax)token.Parent.Parent).StartTag.Name.LocalName.ValueText == ListTagName) { items.AddRange(GetListItems(span)); } if (token.Parent.IsParentKind(SyntaxKind.XmlEmptyElement) & token.Parent.Parent.IsParentKind(SyntaxKind.XmlElement)) { var element = (XmlElementSyntax)token.Parent.Parent.Parent; if (element.StartTag.Name.LocalName.ValueText == ListTagName) { items.AddRange(GetListItems(span)); } } if (token.Parent.Parent.Kind() == SyntaxKind.XmlElement && ((XmlElementSyntax)token.Parent.Parent).StartTag.Name.LocalName.ValueText == ListHeaderTagName) { items.AddRange(GetListHeaderItems(span)); } if (token.Parent.Parent is DocumentationCommentTriviaSyntax) { items.AddRange(GetTopLevelSingleUseNames(parentTrivia, span)); items.AddRange(GetTopLevelRepeatableItems(span)); } } if (token.Parent.Kind() == SyntaxKind.XmlElementStartTag) { var startTag = (XmlElementStartTagSyntax)token.Parent; if (token == startTag.GreaterThanToken && startTag.Name.LocalName.ValueText == ListTagName) { items.AddRange(GetListItems(span)); } if (token == startTag.GreaterThanToken && startTag.Name.LocalName.ValueText == ListHeaderTagName) { items.AddRange(GetListHeaderItems(span)); } } items.AddRange(GetAlwaysVisibleItems(span)); return items; }
public async Task <CompletionList> GetCompletionListAsync( Document document, int position, CompletionTriggerInfo triggerInfo, OptionSet options, IEnumerable <CompletionListProvider> providers, CancellationToken cancellationToken) { options = options ?? document.Project.Solution.Workspace.Options; providers = providers ?? GetDefaultCompletionProviders(); var completionProviderToIndex = GetCompletionProviderToIndex(providers); var completionRules = GetCompletionRules(); var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); IEnumerable <CompletionListProvider> triggeredProviders; switch (triggerInfo.TriggerReason) { case CompletionTriggerReason.TypeCharCommand: triggeredProviders = providers.Where(p => p.IsTriggerCharacter(text, position - 1, options)).ToList(); break; case CompletionTriggerReason.BackspaceOrDeleteCommand: triggeredProviders = this.TriggerOnBackspace(text, position, triggerInfo, options) ? providers : SpecializedCollections.EmptyEnumerable <CompletionListProvider>(); break; default: triggeredProviders = providers; break; } // Now, ask all the triggered providers if they can provide a group. var providersAndLists = new List <ProviderList>(); foreach (var provider in triggeredProviders) { var completionList = await GetCompletionListAsync(provider, document, position, triggerInfo, options, cancellationToken).ConfigureAwait(false); if (completionList != null) { providersAndLists.Add(new ProviderList { Provider = provider, List = completionList }); } } // See if there was a group provided that was exclusive and had items in it. If so, then // that's all we'll return. var firstExclusiveList = providersAndLists.FirstOrDefault( t => t.List.IsExclusive && t.List.Items.Any()); if (firstExclusiveList != null) { return(MergeAndPruneCompletionLists(SpecializedCollections.SingletonEnumerable(firstExclusiveList.List), completionRules)); } // If no exclusive providers provided anything, then go through the remaining // triggered list and see if any provide items. var nonExclusiveLists = providersAndLists.Where(t => !t.List.IsExclusive).ToList(); // If we still don't have any items, then we're definitely done. if (!nonExclusiveLists.Any(g => g.List.Items.Any())) { return(null); } // If we do have items, then ask all the other (non exclusive providers) if they // want to augment the items. var usedProviders = nonExclusiveLists.Select(g => g.Provider); var nonUsedProviders = providers.Except(usedProviders); var nonUsedNonExclusiveProviders = new List <ProviderList>(); foreach (var provider in nonUsedProviders) { var completionList = await GetCompletionListAsync(provider, document, position, triggerInfo, options, cancellationToken).ConfigureAwait(false); if (completionList != null && !completionList.IsExclusive) { nonUsedNonExclusiveProviders.Add(new ProviderList { Provider = provider, List = completionList }); } } var allProvidersAndLists = nonExclusiveLists.Concat(nonUsedNonExclusiveProviders).ToList(); if (allProvidersAndLists.Count == 0) { return(null); } // Providers are ordered, but we processed them in our own order. Ensure that the // groups are properly ordered based on the original providers. allProvidersAndLists.Sort((p1, p2) => completionProviderToIndex[p1.Provider] - completionProviderToIndex[p2.Provider]); return(MergeAndPruneCompletionLists(allProvidersAndLists.Select(g => g.List), completionRules)); }
/// <summary> /// Returns true if the completion item should be "soft" selected, or false if it should be "hard" /// selected. /// </summary> public virtual bool ShouldSoftSelectItem(CompletionItem item, string filterText, CompletionTriggerInfo triggerInfo) { return(filterText.Length == 0 && !item.Preselect); }
protected abstract bool TriggerOnBackspace(SourceText text, int position, CompletionTriggerInfo triggerInfo, OptionSet options);
/// <summary> /// Returns true if item1 is a better completion item than item2 given the provided filter /// text, or false if it is not better. /// </summary> public virtual bool IsBetterFilterMatch(CompletionItem item1, CompletionItem item2, string filterText, CompletionTriggerInfo triggerInfo, CompletionFilterReason filterReason) { var patternMatcher = GetPatternMatcher(_completionService.GetCultureSpecificQuirks(filterText)); var match1 = patternMatcher.GetFirstMatch(_completionService.GetCultureSpecificQuirks(item1.FilterText)); var match2 = patternMatcher.GetFirstMatch(_completionService.GetCultureSpecificQuirks(item2.FilterText)); if (match1 != null && match2 != null) { var result = CompareMatches(match1.Value, match2.Value, item1, item2); if (result != 0) { return(result < 0); } } else if (match1 != null) { return(true); } else if (match2 != null) { return(false); } // If they both seemed just as good, but they differ on preselection, then // item1 is better if it is preselected, otherwise it it worse. if (item1.Preselect != item2.Preselect) { return(item1.Preselect); } // Prefer things with a keyword glyph, if the filter texts are the same. if (item1.Glyph != item2.Glyph && item1.FilterText == item2.FilterText) { return(item1.Glyph == Glyph.Keyword); } // They matched on everything, including preselection values. Item1 is better if it // has a lower MRU index. var item1MRUIndex = _completionService.GetMRUIndex(item1); var item2MRUIndex = _completionService.GetMRUIndex(item2); // The one with the lower index is the better one. return(item1MRUIndex < item2MRUIndex); }
public static Model CreateModel( DisconnectedBufferGraph disconnectedBufferGraph, TextSpan defaultTrackingSpanInSubjectBuffer, ImmutableArray<CompletionItem> totalItems, CompletionItem selectedItem, bool isHardSelection, bool isUnique, bool useSuggestionCompletionMode, CompletionItem builder, CompletionTriggerInfo triggerInfo, ICompletionService completionService, Workspace workspace) { var updatedTotalItems = totalItems; CompletionItem updatedSelectedItem = selectedItem; CompletionItem updatedBuilder = builder; CompletionItem updatedDefaultBuilder = GetDefaultBuilder(defaultTrackingSpanInSubjectBuffer); if (completionService != null && workspace != null && workspace.Kind != WorkspaceKind.Interactive && // TODO (https://github.com/dotnet/roslyn/issues/5107): support in interactive workspace.Options.GetOption(InternalFeatureOnOffOptions.Snippets) && triggerInfo.TriggerReason != CompletionTriggerReason.Snippets) { // In order to add snippet expansion notes to completion item descriptions, update // all of the provided CompletionItems to DescriptionModifyingCompletionItem which will proxy // requests to the original completion items and add the snippet expansion note to // the description if necessary. We won't do this if the list was triggered to show // snippet shortcuts. var updatedTotalItemsBuilder = ImmutableArray.CreateBuilder<CompletionItem>(); updatedSelectedItem = null; foreach (var item in totalItems) { var updatedItem = new DescriptionModifyingCompletionItem(item, completionService, workspace); updatedTotalItemsBuilder.Add(updatedItem); if (item == selectedItem) { updatedSelectedItem = updatedItem; } } updatedTotalItems = updatedTotalItemsBuilder.AsImmutable(); updatedBuilder = null; if (builder != null) { updatedBuilder = new DescriptionModifyingCompletionItem(builder, completionService, workspace); } updatedDefaultBuilder = new DescriptionModifyingCompletionItem( GetDefaultBuilder(defaultTrackingSpanInSubjectBuffer), completionService, workspace); } return new Model( disconnectedBufferGraph, updatedTotalItems, updatedTotalItems, updatedSelectedItem, isHardSelection, isUnique, useSuggestionCompletionMode, updatedBuilder, updatedDefaultBuilder, triggerInfo, GetDefaultTrackingSpanEnd(defaultTrackingSpanInSubjectBuffer, disconnectedBufferGraph), completionService.DismissIfEmpty); }
public static Model CreateModel( DisconnectedBufferGraph disconnectedBufferGraph, TextSpan defaultTrackingSpanInSubjectBuffer, IList<CompletionItem> totalItems, CompletionItem selectedItem, bool isHardSelection, bool isUnique, bool useSuggestionCompletionMode, CompletionItem builder, CompletionTriggerInfo triggerInfo, ICompletionService completionService, Workspace workspace) { var updatedTotalItems = totalItems; CompletionItem updatedSelectedItem = selectedItem; CompletionItem updatedBuilder = builder; CompletionItem updatedDefaultBuilder = GetDefaultBuilder(defaultTrackingSpanInSubjectBuffer); if (completionService != null && workspace != null && triggerInfo.TriggerReason != CompletionTriggerReason.Snippets) { // In order to add snippet expansion notes to completion item descriptions, update // all of the provided CompletionItems to DisplayCompletionItems which will proxy // requests to the original completion items and add the snippet expansion note to // the description if necessary. We won't do this if the list was triggered to show // snippet shorcuts. updatedTotalItems = new List<CompletionItem>(); updatedSelectedItem = null; foreach (var item in totalItems) { var updatedItem = new DescriptionModifyingCompletionItem(item, completionService, workspace); updatedTotalItems.Add(updatedItem); if (item == selectedItem) { updatedSelectedItem = updatedItem; } } updatedBuilder = null; if (builder != null) { updatedBuilder = new DescriptionModifyingCompletionItem(builder, completionService, workspace); } updatedDefaultBuilder = new DescriptionModifyingCompletionItem( GetDefaultBuilder(defaultTrackingSpanInSubjectBuffer), completionService, workspace); } return new Model( disconnectedBufferGraph, updatedTotalItems, updatedTotalItems, updatedSelectedItem, isHardSelection, isUnique, useSuggestionCompletionMode, updatedBuilder, updatedDefaultBuilder, triggerInfo, GetDefaultTrackingSpanEnd(defaultTrackingSpanInSubjectBuffer, disconnectedBufferGraph), completionService.DismissIfEmpty); }
private async Task <IEnumerable <CompletionItemGroup> > GetGroupsAsync( Document documentOpt, SourceText text, int position, CompletionTriggerInfo triggerInfo, IEnumerable <ICompletionProvider> completionProviders, OptionSet options, CancellationToken cancellationToken) { completionProviders = completionProviders ?? this.GetDefaultCompletionProviders(); var completionProviderToIndex = GetCompletionProviderToIndex(completionProviders); IEnumerable <ICompletionProvider> triggeredProviders; switch (triggerInfo.TriggerReason) { case CompletionTriggerReason.TypeCharCommand: triggeredProviders = completionProviders.Where(p => p.IsTriggerCharacter(text, position - 1, options)).ToList(); break; case CompletionTriggerReason.BackspaceOrDeleteCommand: triggeredProviders = this.TriggerOnBackspace(text, position, triggerInfo, options) ? completionProviders : SpecializedCollections.EmptyEnumerable <ICompletionProvider>(); break; default: triggeredProviders = completionProviders; break; } // Now, ask all the triggered providers if they can provide a group. var providersAndGroups = new List <ProviderGroup>(); foreach (var p in triggeredProviders) { var g = await GetGroupAsync(p, documentOpt, text, position, triggerInfo, cancellationToken).ConfigureAwait(false); if (g != null) { providersAndGroups.Add(new ProviderGroup { Provider = p, Group = g }); } } // See if there was a group provided that was exclusive and had items in it. If so, then // that's all we'll return. var firstExclusiveGroup = providersAndGroups.FirstOrDefault( t => t.Group.IsExclusive && t.Group.Items.Any()); if (firstExclusiveGroup != null) { return(SpecializedCollections.SingletonEnumerable(firstExclusiveGroup.Group)); } else { // If no exclusive providers provided anything, then go through the remaining // triggered list and see if any provide items. var nonExclusiveGroups = providersAndGroups.Where(t => !t.Group.IsExclusive).ToList(); // If we still don't have any items, then we're definitely done. if (!nonExclusiveGroups.Any(g => g.Group.Items.Any())) { return(null); } // If we do have items, then ask all the other (non exclusive providers) if they // want to augment the items. var augmentTriggerInfo = triggerInfo.WithIsAugment(true); var usedProviders = nonExclusiveGroups.Select(g => g.Provider); var nonUsedProviders = completionProviders.Except(usedProviders); var nonUsedNonExclusiveProviders = new List <ProviderGroup>(); foreach (var p in nonUsedProviders) { var g = await GetGroupAsync(p, documentOpt, text, position, augmentTriggerInfo, cancellationToken).ConfigureAwait(false); if (g != null && !g.IsExclusive) { nonUsedNonExclusiveProviders.Add(new ProviderGroup { Provider = p, Group = g }); } } var allGroups = nonExclusiveGroups.Concat(nonUsedNonExclusiveProviders).ToList(); if (allGroups.Count == 0) { return(null); } // Providers are ordered, but we processed them in our own order. Ensure that the // groups are properly ordered based on the original providers. allGroups.Sort((p1, p2) => completionProviderToIndex[p1.Provider] - completionProviderToIndex[p2.Provider]); return(allGroups.Select(g => g.Group)); } }