private static void AddTypes(HashSet <CompletionItem> completionItems, int contextPosition, SemanticModel semanticModel, CancellationToken cancellationToken) { // We have to find the set of types that meet the criteria listed in // https://github.com/dotnet/csharplang/blob/master/proposals/csharp-9.0/function-pointers.md#mapping-the-calling_convention_specifier-to-a-callkind // We skip the check of an type being in the core assembly since that's not really necessary for our work. var compilerServicesNamespace = semanticModel.Compilation.GlobalNamespace.GetQualifiedNamespace("System.Runtime.CompilerServices"); if (compilerServicesNamespace == null) { return; } foreach (var type in compilerServicesNamespace.GetTypeMembers()) { cancellationToken.ThrowIfCancellationRequested(); const string CallConvPrefix = "CallConv"; if (type.DeclaredAccessibility == Accessibility.Public && type.Name.StartsWith(CallConvPrefix)) { var displayName = type.Name.Substring(CallConvPrefix.Length); completionItems.Add( SymbolCompletionItem.CreateWithSymbolId( displayName, ImmutableArray.Create(type), rules: CompletionItemRules.Default, contextPosition)); } } }
public static CompletionItem CreateSymbolCompletionItem( string displayText, IReadOnlyList <ISymbol> symbols, CompletionItemRules rules, int contextPosition, string?sortText = null, string?insertionText = null, string?filterText = null, SupportedPlatformData?supportedPlatforms = null, ImmutableDictionary <string, string>?properties = null, ImmutableArray <string> tags = default ) => SymbolCompletionItem.CreateWithSymbolId( displayText, displayTextSuffix: null, symbols, rules, contextPosition, sortText, insertionText, filterText, displayTextPrefix: null, inlineDescription: null, glyph: null, supportedPlatforms, properties, tags );
private static void AddItems( ImmutableArray <INamedTypeSymbol> inferredTypes, int index, CompletionContext context, int spanStart ) { foreach (var type in inferredTypes) { if (index >= type.TupleElements.Length) { return; } // Note: the filter text does not include the ':'. We want to ensure that if // the user types the name exactly (up to the colon) that it is selected as an // exact match. var field = type.TupleElements[index]; context.AddItem( SymbolCompletionItem.CreateWithSymbolId( displayText: field.Name, displayTextSuffix: ColonString, symbols: ImmutableArray.Create(field), rules: CompletionItemRules.Default, contextPosition: spanStart, filterText: field.Name ) ); } }
private static async Task <IEnumerable <CompletionItem> > GetNameColonItemsAsync( CompletionContext context, SemanticModel semanticModel, SyntaxToken token, AttributeSyntax attributeSyntax, ISet <string> existingNamedParameters ) { var parameterLists = GetParameterLists( semanticModel, context.Position, attributeSyntax, context.CancellationToken ); parameterLists = parameterLists.Where(pl => IsValid(pl, existingNamedParameters)); var text = await semanticModel.SyntaxTree .GetTextAsync(context.CancellationToken) .ConfigureAwait(false); return(from pl in parameterLists from p in pl where !existingNamedParameters.Contains(p.Name) select SymbolCompletionItem.CreateWithSymbolId( displayText : p.Name.ToIdentifierToken().ToString(), displayTextSuffix : ColonString, insertionText : null, symbols : ImmutableArray.Create(p), contextPosition : token.SpanStart, sortText : p.Name, rules : CompletionItemRules.Default )); }
private void AddConversion( CompletionContext context, SemanticModel semanticModel, int position, IMethodSymbol conversion ) { var(symbols, properties) = GetConversionSymbolsAndProperties(context, conversion); var targetTypeName = conversion.ReturnType.ToMinimalDisplayString( semanticModel, position ); context.AddItem( SymbolCompletionItem.CreateWithSymbolId( displayTextPrefix: "(", displayText: targetTypeName, displayTextSuffix: ")", filterText: targetTypeName, sortText: SortText(ConversionSortingGroupIndex, targetTypeName), glyph: Glyph.Operator, symbols: symbols, rules: CompletionItemRules.Default, contextPosition: position, properties: properties ) ); }
public override async Task ProvideCompletionsAsync(CompletionContext context) { var document = context.Document; var position = context.Position; var cancellationToken = context.CancellationToken; var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); // For `is { Property.Property2.$$`, we get: // - the property pattern clause `{ ... }` and // - the member access before the last dot `Property.Property2` (or null) var(propertyPatternClause, memberAccess) = TryGetPropertyPatternClause(tree, position, cancellationToken); if (propertyPatternClause is null) { return; } var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); var propertyPatternType = semanticModel.GetTypeInfo((PatternSyntax)propertyPatternClause.Parent, cancellationToken).ConvertedType; // For simple property patterns, the type we want is the "input type" of the property pattern, ie the type of `c` in `c is { $$ }`. // For extended property patterns, we get the type by following the chain of members that we have so far, ie // the type of `c.Property` for `c is { Property.$$ }` and the type of `c.Property1.Property2` for `c is { Property1.Property2.$$ }`. var type = GetMemberAccessType(propertyPatternType, memberAccess, document, semanticModel, position); if (type is null) { return; } // Find the members that can be tested. var members = GetCandidatePropertiesAndFields(document, semanticModel, position, type); members = members.WhereAsArray(m => m.IsEditorBrowsable(document.ShouldHideAdvancedMembers(), semanticModel.Compilation)); if (memberAccess is null) { // Filter out those members that have already been typed as simple (not extended) properties var alreadyTestedMembers = new HashSet <string>(propertyPatternClause.Subpatterns.Select( p => p.NameColon?.Name.Identifier.ValueText).Where(s => !string.IsNullOrEmpty(s))); members = members.WhereAsArray(m => !alreadyTestedMembers.Contains(m.Name)); } foreach (var member in members) { const string ColonString = ":"; context.AddItem(SymbolCompletionItem.CreateWithSymbolId( displayText: member.Name.EscapeIdentifier(), displayTextSuffix: ColonString, insertionText: null, symbols: ImmutableArray.Create(member), contextPosition: context.Position, rules: s_rules)); } return;
public override async Task ProvideCompletionsAsync(CompletionContext context) { var document = context.Document; var position = context.Position; var cancellationToken = context.CancellationToken; var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var token = TryGetOpenBraceOrCommaInPropertyPatternClause(tree, position, cancellationToken); if (token == default || !(token.Parent.Parent is PatternSyntax)) { return; } var semanticModel = await document.GetSemanticModelForSpanAsync(new TextSpan(position, length : 0), cancellationToken).ConfigureAwait(false); var pattern = (PatternSyntax)token.Parent.Parent; var type = semanticModel.GetTypeInfo(pattern, cancellationToken).ConvertedType; if (type == null) { return; } // Find the members that can be tested. IEnumerable <ISymbol> members = semanticModel.LookupSymbols(position, type); members = members.Where(m => m.CanBeReferencedByName && IsFieldOrReadableProperty(m) && !m.IsImplicitlyDeclared && !m.IsStatic); // Filter out those members that have already been typed var propertyPatternClause = (PropertyPatternClauseSyntax)token.Parent; // List the members that are already tested in this property sub-pattern var alreadyTestedMembers = new HashSet <string>(propertyPatternClause.Subpatterns.Select( p => p.NameColon?.Name.Identifier.ValueText).Where(s => !string.IsNullOrEmpty(s))); var untestedMembers = members.Where(m => !alreadyTestedMembers.Contains(m.Name) && m.IsEditorBrowsable(document.ShouldHideAdvancedMembers(), semanticModel.Compilation)); foreach (var untestedMember in untestedMembers) { const string ColonString = ":"; context.AddItem(SymbolCompletionItem.CreateWithSymbolId( displayText: untestedMember.Name.EscapeIdentifier(), displayTextSuffix: ColonString, insertionText: null, symbols: ImmutableArray.Create(untestedMember), contextPosition: token.GetLocation().SourceSpan.Start, rules: s_rules)); } }
void Analyze(CompletionContext context, SemanticModel model, SyntaxNode node, ISymbol within, ParameterListSyntax parameterList, ISymbol symbol, HashSet <string> addedSymbols, CancellationToken cancellationToken) { var type = CheckParameterList(model, parameterList, symbol, cancellationToken); if (type == null) { return; } var startType = type; var typeString = CSharpAmbience.SafeMinimalDisplayString(type, model, context.CompletionListSpan.Start, Ambience.LabelFormat); var pDict = ImmutableDictionary <string, string> .Empty; if (typeString != null) { pDict = pDict.Add("CastTypeString", typeString); } pDict = pDict.Add("DescriptionMarkup", "- <span foreground=\"darkgray\" size='small'>" + GettextCatalog.GetString("Cast to '{0}'", type.Name) + "</span>"); pDict = pDict.Add("NodeString", node.ToString()); while (type.SpecialType != SpecialType.System_Object) { foreach (var member in type.GetMembers()) { if (member.IsImplicitlyDeclared || member.IsStatic) { continue; } if (member.IsOrdinaryMethod() || member.Kind == SymbolKind.Field || member.Kind == SymbolKind.Property) { if (member.IsAccessibleWithin(within)) { var completionData = SymbolCompletionItem.CreateWithSymbolId( member.Name, new [] { member }, CompletionItemRules.Default, context.Position, properties: pDict ); if (addedSymbols.Contains(completionData.DisplayText)) { continue; } addedSymbols.Add(completionData.DisplayText); context.AddItem(completionData); } } } type = type.BaseType; } }
private void AddIndexers(CompletionContext context, ImmutableArray <ISymbol> indexers) { if (indexers.Length == 0) { return; } context.AddItem(SymbolCompletionItem.CreateWithSymbolId( displayText: "this", displayTextSuffix: "[]", filterText: "this", sortText: "this", symbols: indexers, rules: CompletionItemRules.Default, contextPosition: context.Position, properties: IndexerProperties)); }
private async Task <ImmutableArray <CompletionItem> > GetNameEqualsItemsAsync( CompletionContext context, SemanticModel semanticModel, SyntaxToken token, AttributeSyntax attributeSyntax, ISet <string> existingNamedParameters) { var attributeNamedParameters = GetAttributeNamedParameters(semanticModel, context.Position, attributeSyntax, context.CancellationToken); var unspecifiedNamedParameters = attributeNamedParameters.Where(p => !existingNamedParameters.Contains(p.Name)); var text = await semanticModel.SyntaxTree.GetTextAsync(context.CancellationToken).ConfigureAwait(false); var q = from p in attributeNamedParameters where !existingNamedParameters.Contains(p.Name) select SymbolCompletionItem.CreateWithSymbolId( displayText : p.Name.ToIdentifierToken().ToString() + SpaceEqualsString, insertionText : null, symbols : ImmutableArray.Create(p), contextPosition : token.SpanStart, sortText : p.Name, rules : _spaceItemFilterRule); return(q.ToImmutableArray()); }
public override async Task ProvideCompletionsAsync(CompletionContext context) { var document = context.Document; var position = context.Position; var cancellationToken = context.CancellationToken; var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (syntaxTree.IsInNonUserCode(position, cancellationToken)) { return; } var token = syntaxTree .FindTokenOnLeftOfPosition(position, cancellationToken) .GetPreviousTokenIfTouchingWord(position); if (!token.IsKind(SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken, SyntaxKind.CommaToken)) { return; } var argumentList = token.Parent as BaseArgumentListSyntax; if (argumentList == null) { return; } var semanticModel = await document.GetSemanticModelForNodeAsync(argumentList, cancellationToken).ConfigureAwait(false); var parameterLists = GetParameterLists(semanticModel, position, argumentList.Parent, cancellationToken); if (parameterLists == null) { return; } var existingNamedParameters = GetExistingNamedParameters(argumentList, position); parameterLists = parameterLists.Where(pl => IsValid(pl, existingNamedParameters)); var unspecifiedParameters = parameterLists.SelectMany(pl => pl) .Where(p => !existingNamedParameters.Contains(p.Name)) .Distinct(this); if (!unspecifiedParameters.Any()) { return; } // Consider refining this logic to mandate completion with an argument name, if preceded by an out-of-position name // See https://github.com/dotnet/roslyn/issues/20657 var languageVersion = ((CSharpParseOptions)document.Project.ParseOptions).LanguageVersion; if (languageVersion < LanguageVersion.CSharp7_2 && token.IsMandatoryNamedParameterPosition()) { context.IsExclusive = true; } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var workspace = document.Project.Solution.Workspace; foreach (var parameter in unspecifiedParameters) { // Note: the filter text does not include the ':'. We want to ensure that if // the user types the name exactly (up to the colon) that it is selected as an // exact match. var escapedName = parameter.Name.ToIdentifierToken().ToString(); context.AddItem(SymbolCompletionItem.CreateWithSymbolId( displayText: escapedName + ColonString, symbols: ImmutableArray.Create(parameter), rules: s_rules.WithMatchPriority(SymbolMatchPriority.PreferNamedArgument), contextPosition: token.SpanStart, filterText: escapedName)); } }
public override async Task ProvideCompletionsAsync(CompletionContext context) { try { var document = context.Document; var position = context.Position; var cancellationToken = context.CancellationToken; var syntaxTree = await document.GetRequiredSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); var syntaxFacts = document.GetRequiredLanguageService <ISyntaxFactsService>(); var semanticFacts = document.GetRequiredLanguageService <ISemanticFactsService>(); if (syntaxFacts.IsInNonUserCode(syntaxTree, position, cancellationToken) || syntaxFacts.IsPreProcessorDirectiveContext(syntaxTree, position, cancellationToken)) { return; } var targetToken = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken) .GetPreviousTokenIfTouchingWord(position); if (!syntaxTree.IsRightOfDotOrArrowOrColonColon(position, targetToken, cancellationToken)) { return; } var node = targetToken.Parent; if (!node.IsKind(SyntaxKind.ExplicitInterfaceSpecifier, out ExplicitInterfaceSpecifierSyntax? specifierNode)) { return; } // Bind the interface name which is to the left of the dot var name = specifierNode.Name; var semanticModel = await document.ReuseExistingSpeculativeModelAsync(position, cancellationToken).ConfigureAwait(false); var symbol = semanticModel.GetSymbolInfo(name, cancellationToken).Symbol as ITypeSymbol; if (symbol?.TypeKind != TypeKind.Interface) { return; } // We're going to create a entry for each one, including the signature var namePosition = name.SpanStart; foreach (var member in symbol.GetMembers()) { if (!member.IsAbstract && !member.IsVirtual) { continue; } if (member.IsAccessor() || member.Kind == SymbolKind.NamedType || !semanticModel.IsAccessible(node.SpanStart, member)) { continue; } var memberString = member.ToMinimalDisplayString(semanticModel, namePosition, s_signatureDisplayFormat); // Split the member string into two parts (generally the name, and the signature portion). We want // the split so that other features (like spell-checking), only look at the name portion. var(displayText, displayTextSuffix) = SplitMemberName(memberString); context.AddItem(SymbolCompletionItem.CreateWithSymbolId( displayText, displayTextSuffix, insertionText: memberString, symbols: ImmutableArray.Create <ISymbol>(member), contextPosition: position, rules: CompletionItemRules.Default)); } } catch (Exception e) when(FatalError.ReportWithoutCrashUnlessCanceled(e)) { // nop } }
public override async Task ProvideCompletionsAsync(CompletionContext context) { var document = context.Document; var position = context.Position; var options = context.Options; var cancellationToken = context.CancellationToken; var span = new TextSpan(position, length: 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; } if (!syntaxTree.IsRightOfDotOrArrowOrColonColon(position, cancellationToken)) { return; } var node = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken) .GetPreviousTokenIfTouchingWord(position) .Parent; if (node.Kind() != SyntaxKind.ExplicitInterfaceSpecifier) { return; } // Bind the interface name which is to the left of the dot var name = ((ExplicitInterfaceSpecifierSyntax)node).Name; var symbol = semanticModel.GetSymbolInfo(name, cancellationToken).Symbol as ITypeSymbol; if (symbol?.TypeKind != TypeKind.Interface) { return; } var members = semanticModel.LookupSymbols( position: name.SpanStart, container: symbol) .WhereAsArray(s => !s.IsStatic) .FilterToVisibleAndBrowsableSymbols(options.GetOption(CompletionOptions.HideAdvancedMembers, semanticModel.Language), semanticModel.Compilation); // We're going to create a entry for each one, including the signature var namePosition = name.SpanStart; var text = await syntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false); foreach (var member in members) { var displayText = member.ToMinimalDisplayString( semanticModel, namePosition, s_signatureDisplayFormat); var insertionText = displayText; var item = SymbolCompletionItem.CreateWithSymbolId( displayText, insertionText: insertionText, symbol: member, contextPosition: position, rules: CompletionItemRules.Default); item = item.AddProperty(InsertionTextOnOpenParen, member.Name); context.AddItem(item); } }
private static async Task HandleSingleTypeAsync(CompletionContext context, SemanticModel semanticModel, SyntaxToken token, ITypeSymbol type, CancellationToken cancellationToken) { // If we have a Nullable<T>, unwrap it. if (type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) { var typeArg = type.GetTypeArguments().FirstOrDefault(); if (typeArg == null) return; type = typeArg; } var position = context.Position; if (type.TypeKind != TypeKind.Enum) { var enumType = TryGetEnumTypeInEnumInitializer(semanticModel, token, type, cancellationToken) ?? TryGetCompletionListType(type, semanticModel.GetEnclosingNamedType(position, cancellationToken), semanticModel.Compilation); if (enumType == null) return; type = enumType; } var options = context.Options; var hideAdvancedMembers = options.GetOption(CompletionOptions.HideAdvancedMembers, semanticModel.Language); if (!type.IsEditorBrowsable(hideAdvancedMembers, semanticModel.Compilation)) return; // Does type have any aliases? var alias = await type.FindApplicableAliasAsync(position, semanticModel, cancellationToken).ConfigureAwait(false); var displayText = alias != null ? alias.Name : type.ToMinimalDisplayString(semanticModel, position); // Add the enum itself. var symbol = alias ?? type; var sortText = symbol.Name; context.AddItem(SymbolCompletionItem.CreateWithSymbolId( displayText, displayTextSuffix: "", symbols: ImmutableArray.Create(symbol), rules: s_enumTypeRules, contextPosition: position, sortText: sortText)); // And now all the accessible members of the enum. if (type.TypeKind == TypeKind.Enum) { // We'll want to build a list of the actual enum members and all accessible instances of that enum, too var index = 0; var fields = type.GetMembers().OfType<IFieldSymbol>().Where(f => f.IsConst).Where(f => f.HasConstantValue); foreach (var field in fields.OrderBy(f => IntegerUtilities.ToInt64(f.ConstantValue))) { index++; if (!field.IsEditorBrowsable(hideAdvancedMembers, semanticModel.Compilation)) continue; var memberDisplayName = $"{displayText}.{field.Name}"; context.AddItem(SymbolCompletionItem.CreateWithSymbolId( displayText: memberDisplayName, displayTextSuffix: "", symbols: ImmutableArray.Create<ISymbol>(field), rules: CompletionItemRules.Default, contextPosition: position, sortText: $"{sortText}_{index:0000}", filterText: memberDisplayName)); } } }
public override async Task ProvideCompletionsAsync(CompletionContext context) { var document = context.Document; var position = context.Position; var cancellationToken = context.CancellationToken; var syntaxTree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (syntaxTree.IsInNonUserCode(position, cancellationToken)) { return; } var token = syntaxTree .FindTokenOnLeftOfPosition(position, cancellationToken) .GetPreviousTokenIfTouchingWord(position); if (!token.IsKind(SyntaxKind.OpenParenToken, SyntaxKind.OpenBracketToken, SyntaxKind.CommaToken)) { return; } var argumentList = token.Parent as BaseArgumentListSyntax; if (argumentList == null) { return; } var semanticModel = await document.GetSemanticModelForNodeAsync(argumentList, cancellationToken).ConfigureAwait(false); var parameterLists = GetParameterLists(semanticModel, position, argumentList.Parent, cancellationToken); if (parameterLists == null) { return; } var existingNamedParameters = GetExistingNamedParameters(argumentList, position); parameterLists = parameterLists.Where(pl => IsValid(pl, existingNamedParameters)); var unspecifiedParameters = parameterLists.SelectMany(pl => pl) .Where(p => !existingNamedParameters.Contains(p.Name)) .Distinct(this); if (!unspecifiedParameters.Any()) { return; } if (token.IsMandatoryNamedParameterPosition()) { context.IsExclusive = true; } var text = await document.GetTextAsync(cancellationToken).ConfigureAwait(false); var workspace = document.Project.Solution.Workspace; foreach (var parameter in unspecifiedParameters) { // Note: the filter text does not include the ':'. We want to ensure that if // the user types the name exactly (up to the colon) that it is selected as an // exact match. var escapedName = parameter.Name.ToIdentifierToken().ToString(); context.AddItem(SymbolCompletionItem.CreateWithSymbolId( displayText: escapedName + ColonString, insertionText: null, symbol: parameter, contextPosition: token.SpanStart, filterText: escapedName, rules: s_rules, matchPriority: SymbolMatchPriority.PreferNamedArgument)); } }
public override async Task ProvideCompletionsAsync(CompletionContext context) { try { var document = context.Document; var position = context.Position; var options = context.Options; var cancellationToken = context.CancellationToken; var tree = await document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); if (tree.IsInNonUserCode(position, cancellationToken)) { return; } var token = tree.FindTokenOnLeftOfPosition(position, cancellationToken) .GetPreviousTokenIfTouchingWord(position); if (token.IsMandatoryNamedParameterPosition()) { return; } // Don't show up within member access // This previously worked because the type inferrer didn't work // in member access expressions. // The regular SymbolCompletionProvider will handle completion after . if (token.IsKind(SyntaxKind.DotToken)) { return; } var typeInferenceService = document.GetLanguageService <ITypeInferenceService>(); Contract.ThrowIfNull(typeInferenceService, nameof(typeInferenceService)); var span = new TextSpan(position, 0); var semanticModel = await document.GetSemanticModelForSpanAsync(span, cancellationToken).ConfigureAwait(false); var type = typeInferenceService.InferType(semanticModel, position, objectAsDefault: true, cancellationToken: cancellationToken); // If we have a Nullable<T>, unwrap it. if (type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) { type = type.GetTypeArguments().FirstOrDefault(); if (type == null) { return; } } if (type.TypeKind != TypeKind.Enum) { type = TryGetEnumTypeInEnumInitializer(semanticModel, token, type, cancellationToken) ?? TryGetCompletionListType(type, semanticModel.GetEnclosingNamedType(position, cancellationToken), semanticModel.Compilation); if (type == null) { return; } } if (!type.IsEditorBrowsable(options.GetOption(CompletionOptions.HideAdvancedMembers, semanticModel.Language), semanticModel.Compilation)) { return; } // Does type have any aliases? var alias = await type.FindApplicableAlias(position, semanticModel, cancellationToken).ConfigureAwait(false); var displayService = document.GetLanguageService <ISymbolDisplayService>(); var displayText = alias != null ? alias.Name : displayService.ToMinimalDisplayString(semanticModel, position, type); var workspace = document.Project.Solution.Workspace; var text = await semanticModel.SyntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false); var item = SymbolCompletionItem.CreateWithSymbolId( displayText: displayText, symbols: ImmutableArray.Create(alias ?? type), rules: s_rules.WithMatchPriority(MatchPriority.Preselect), contextPosition: position); context.AddItem(item); } catch (Exception e) when(FatalError.ReportUnlessCanceled(e)) { throw ExceptionUtilities.Unreachable; } }
private static async Task HandleSingleTypeAsync( CompletionContext context, SemanticModel semanticModel, SyntaxToken token, ITypeSymbol type, CancellationToken cancellationToken ) { // If we have a Nullable<T>, unwrap it. if (type.OriginalDefinition.SpecialType == SpecialType.System_Nullable_T) { var typeArg = type.GetTypeArguments().FirstOrDefault(); if (typeArg == null) { return; } type = typeArg; } // When true, this completion provider shows both the type (e.g. DayOfWeek) and its qualified members (e.g. // DayOfWeek.Friday). We set this to false for enum-like cases (static members of structs and classes) so we // only show the qualified members in these cases. var showType = true; var position = context.Position; var enclosingNamedType = semanticModel.GetEnclosingNamedType( position, cancellationToken ); if (type.TypeKind != TypeKind.Enum) { var enumType = TryGetEnumTypeInEnumInitializer(semanticModel, token, type, cancellationToken) ?? TryGetCompletionListType( type, enclosingNamedType, semanticModel.Compilation ); if (enumType == null) { if ( context.Trigger.Kind == CompletionTriggerKind.Insertion && s_triggerCharacters.Contains(context.Trigger.Character) ) { // This completion provider understands static members of matching types, but doesn't // proactively trigger completion for them to avoid interfering with common typing patterns. return; } // If this isn't an enum or marked with completionlist, also check if it contains static members of // a matching type. These 'enum-like' types have similar characteristics to enum completion, but do // not show the containing type as a separate item in completion. showType = false; enumType = TryGetTypeWithStaticMembers(type); if (enumType == null) { return; } } type = enumType; } var options = context.Options; var hideAdvancedMembers = options.GetOption( CompletionOptions.HideAdvancedMembers, semanticModel.Language ); if (!type.IsEditorBrowsable(hideAdvancedMembers, semanticModel.Compilation)) { return; } // Does type have any aliases? var alias = await type.FindApplicableAliasAsync( position, semanticModel, cancellationToken ) .ConfigureAwait(false); var displayText = alias != null ? alias.Name : type.ToMinimalDisplayString(semanticModel, position); // Add the enum itself. var symbol = alias ?? type; var sortText = symbol.Name; if (showType) { context.AddItem( SymbolCompletionItem.CreateWithSymbolId( displayText, displayTextSuffix: "", symbols: ImmutableArray.Create(symbol), rules: s_enumTypeRules, contextPosition: position, sortText: sortText ) ); } // And now all the accessible members of the enum. if (type.TypeKind == TypeKind.Enum) { // We'll want to build a list of the actual enum members and all accessible instances of that enum, too var index = 0; var fields = type.GetMembers() .OfType <IFieldSymbol>() .Where(f => f.IsConst) .Where(f => f.HasConstantValue); foreach ( var field in fields.OrderBy(f => IntegerUtilities.ToInt64(f.ConstantValue)) ) { index++; if (!field.IsEditorBrowsable(hideAdvancedMembers, semanticModel.Compilation)) { continue; } var memberDisplayName = $"{displayText}.{field.Name}"; context.AddItem( SymbolCompletionItem.CreateWithSymbolId( displayText: memberDisplayName, displayTextSuffix: "", symbols: ImmutableArray.Create <ISymbol>(field), rules: CompletionItemRules.Default, contextPosition: position, sortText: $"{sortText}_{index:0000}", filterText: memberDisplayName ) ); } } else if (enclosingNamedType is not null) { // Build a list of the members with the same type as the target foreach (var member in type.GetMembers()) { ISymbol staticSymbol; ITypeSymbol symbolType; if (member is IFieldSymbol { IsStatic: true } field) { staticSymbol = field; symbolType = field.Type; } else if ( member is IPropertySymbol { IsStatic: true, IsIndexer: false } property ) { staticSymbol = property; symbolType = property.Type; }
public override async Task ProvideCompletionsAsync(CompletionContext context) { try { var document = context.Document; var position = context.Position; var options = context.Options; var cancellationToken = context.CancellationToken; var span = new TextSpan(position, length: 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; } var targetToken = syntaxTree.FindTokenOnLeftOfPosition(position, cancellationToken) .GetPreviousTokenIfTouchingWord(position); if (!syntaxTree.IsRightOfDotOrArrowOrColonColon(position, targetToken, cancellationToken)) { return; } var node = targetToken.Parent; if (node.Kind() != SyntaxKind.ExplicitInterfaceSpecifier) { return; } // Bind the interface name which is to the left of the dot var name = ((ExplicitInterfaceSpecifierSyntax)node).Name; var symbol = semanticModel.GetSymbolInfo(name, cancellationToken).Symbol as ITypeSymbol; if (symbol?.TypeKind != TypeKind.Interface) { return; } var members = symbol.GetMembers(); // We're going to create a entry for each one, including the signature var namePosition = name.SpanStart; var text = await syntaxTree.GetTextAsync(cancellationToken).ConfigureAwait(false); foreach (var member in members) { if (member.IsAccessor() || member.Kind == SymbolKind.NamedType || !(member.IsAbstract || member.IsVirtual) || !semanticModel.IsAccessible(node.SpanStart, member)) { continue; } var displayText = member.ToMinimalDisplayString( semanticModel, namePosition, s_signatureDisplayFormat); var insertionText = displayText; var item = SymbolCompletionItem.CreateWithSymbolId( displayText, displayTextSuffix: "", insertionText: insertionText, symbols: ImmutableArray.Create(member), contextPosition: position, rules: CompletionItemRules.Default); item = item.AddProperty(InsertionTextOnOpenParen, member.Name); context.AddItem(item); } } catch (Exception e) when(FatalError.ReportWithoutCrashUnlessCanceled(e)) { // nop } }