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; }
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)); } } }