private static IEnumerable <CompletionItem> GetParameterTypeSnippets()
        {
            yield return(CompletionItemFactory.CreateContextualSnippetCompletion("secureObject", "Secure object", @"object {
  secure: true
}"));

            yield return(CompletionItemFactory.CreateContextualSnippetCompletion("secureString", "Secure string", @"string {
  secure: true
}"));
        }
        private IEnumerable <CompletionItem> GetDeclarationTypeCompletions(BicepCompletionContext context)
        {
            // local function
            IEnumerable <CompletionItem> GetPrimitiveTypeCompletions() =>
            LanguageConstants.DeclarationTypes.Values.Select(type => CompletionItemFactory.CreateTypeCompletion(type));

            if (context.Kind.HasFlag(BicepCompletionContextKind.ParameterType))
            {
                return(GetPrimitiveTypeCompletions().Concat(GetParameterTypeSnippets()));
            }

            if (context.Kind.HasFlag(BicepCompletionContextKind.OutputType))
            {
                return(GetPrimitiveTypeCompletions());
            }

            return(Enumerable.Empty <CompletionItem>());
        }
        private static IEnumerable <CompletionItem> GetValueCompletionsForType(TypeSymbol?propertyType)
        {
            switch (propertyType)
            {
            case PrimitiveType _ when ReferenceEquals(propertyType, LanguageConstants.Bool):
                yield return(CompletionItemFactory.CreateKeywordCompletion(LanguageConstants.TrueKeyword, LanguageConstants.TrueKeyword, preselect: true, CompletionPriority.High));

                yield return(CompletionItemFactory.CreateKeywordCompletion(LanguageConstants.FalseKeyword, LanguageConstants.FalseKeyword, preselect: true, CompletionPriority.High));

                break;

            case StringLiteralType stringLiteral:
                yield return(CompletionItemFactory.CreatePlaintextCompletion(CompletionItemKind.EnumMember, stringLiteral.Name, stringLiteral.Name, preselect: true));

                break;

            case ArrayType _:
                yield return(CompletionItemFactory.CreateSnippetCompletion(CompletionItemKind.Value, "[]", "[$0]", "[]", preselect: true, CompletionPriority.High));

                break;

            case ObjectType _:
                yield return(CompletionItemFactory.CreateSnippetCompletion(CompletionItemKind.Value, "{}", "{$0}", "{}", preselect: true, CompletionPriority.High));

                break;

            case UnionType union:
                var aggregatedCompletions = union.Members.SelectMany(typeRef => GetValueCompletionsForType(typeRef.Type));
                foreach (var completion in aggregatedCompletions)
                {
                    yield return(completion);
                }

                break;
            }
        }
        private IEnumerable <CompletionItem> GetObjectPropertyNameCompletions(SemanticModel model, BicepCompletionContext context)
        {
            if (context.Kind.HasFlag(BicepCompletionContextKind.PropertyName) == false || context.Object == null)
            {
                return(Enumerable.Empty <CompletionItem>());
            }

            // in order to provide completions for property names,
            // we need to establish the type of the object first
            var declaredType = model.GetDeclaredType(context.Object);

            if (declaredType == null)
            {
                return(Enumerable.Empty <CompletionItem>());
            }

            var specifiedPropertyNames = context.Object.ToKnownPropertyNames();

            // exclude read-only properties as they can't be set
            // exclude properties whose name has been specified in the object already
            return(GetProperties(declaredType)
                   .Where(p => p.Flags.HasFlag(TypePropertyFlags.ReadOnly) == false && specifiedPropertyNames.Contains(p.Name) == false)
                   .Select(p => CompletionItemFactory.CreatePropertyNameCompletion(p)));
        }
        private IEnumerable <CompletionItem> GetDeclarationCompletions(BicepCompletionContext context)
        {
            if (context.Kind.HasFlag(BicepCompletionContextKind.DeclarationStart))
            {
                yield return(CompletionItemFactory.CreateKeywordCompletion(LanguageConstants.ParameterKeyword, "Parameter keyword"));

                yield return(CompletionItemFactory.CreateContextualSnippetCompletion(LanguageConstants.ParameterKeyword, "Parameter declaration", "param ${1:Identifier} ${2:Type}"));

                yield return(CompletionItemFactory.CreateContextualSnippetCompletion(LanguageConstants.ParameterKeyword, "Parameter declaration with default value", "param ${1:Identifier} ${2:Type} = ${3:DefaultValue}"));

                yield return(CompletionItemFactory.CreateContextualSnippetCompletion(LanguageConstants.ParameterKeyword, "Parameter declaration with default and allowed values", @"param ${1:Identifier} ${2:Type} {
  default: $3
  allowed: [
    $4
  ]
}"));

                yield return(CompletionItemFactory.CreateContextualSnippetCompletion(LanguageConstants.ParameterKeyword, "Parameter declaration with options", @"param ${1:Identifier} ${2:Type} {
  $0
}"));

                yield return(CompletionItemFactory.CreateContextualSnippetCompletion(LanguageConstants.ParameterKeyword, "Secure string parameter", @"param ${1:Identifier} string {
  secure: true
}"));

                yield return(CompletionItemFactory.CreateKeywordCompletion(LanguageConstants.VariableKeyword, "Variable keyword"));

                yield return(CompletionItemFactory.CreateContextualSnippetCompletion(LanguageConstants.VariableKeyword, "Variable declaration", "var ${1:Identifier} = $0"));

                yield return(CompletionItemFactory.CreateKeywordCompletion(LanguageConstants.ResourceKeyword, "Resource keyword"));

                yield return(CompletionItemFactory.CreateContextualSnippetCompletion(LanguageConstants.ResourceKeyword, "Resource with defaults", @"resource ${1:Identifier} 'Microsoft.${2:Provider}/${3:Type}@${4:Version}' = {
  name: $5
  location: $6
  properties: {
    $0
  }
}"));

                yield return(CompletionItemFactory.CreateContextualSnippetCompletion(LanguageConstants.ResourceKeyword, "Child Resource with defaults", @"resource ${1:Identifier} 'Microsoft.${2:Provider}/${3:ParentType}/${4:ChildType}@${5:Version}' = {
  name: $6
  properties: {
    $0
  }
}"));

                yield return(CompletionItemFactory.CreateContextualSnippetCompletion(LanguageConstants.ResourceKeyword, "Resource without defaults", @"resource ${1:Identifier} 'Microsoft.${2:Provider}/${3:Type}@${4:Version}' = {
  name: $5
  $0
}
"));

                yield return(CompletionItemFactory.CreateContextualSnippetCompletion(LanguageConstants.ResourceKeyword, "Child Resource without defaults", @"resource ${1:Identifier} 'Microsoft.${2:Provider}/${3:ParentType}/${4:ChildType}@${5:Version}' = {
  name: $6
  $0
}"));

                yield return(CompletionItemFactory.CreateKeywordCompletion(LanguageConstants.OutputKeyword, "Output keyword"));

                yield return(CompletionItemFactory.CreateContextualSnippetCompletion(LanguageConstants.OutputKeyword, "Output declaration", "output ${1:Identifier} ${2:Type} = $0"));

                yield return(CompletionItemFactory.CreateKeywordCompletion(LanguageConstants.ModuleKeyword, "Module keyword"));

                yield return(CompletionItemFactory.CreateContextualSnippetCompletion(LanguageConstants.ModuleKeyword, "Module declaration", @"module ${1:Identifier} '${2:Path}' = {
  name: $3
  $0
}"));
            }
        }
        private static IEnumerable <CompletionItem> GetAccessibleSymbolCompletions(SemanticModel model, BicepCompletionContext context)
        {
            // maps insert text to the completion item
            var completions = new Dictionary <string, CompletionItem>();

            var enclosingDeclarationSymbol = context.EnclosingDeclaration == null
                ? null
                : model.GetSymbolInfo(context.EnclosingDeclaration);

            // local function
            void AddSymbolCompletions(IDictionary <string, CompletionItem> result, IEnumerable <Symbol> symbols)
            {
                foreach (var symbol in symbols)
                {
                    if (!result.ContainsKey(symbol.Name) && !ReferenceEquals(symbol, enclosingDeclarationSymbol))
                    {
                        // we have not added a symbol with the same name (avoids duplicate completions)
                        // and the symbol is different than the enclosing declaration (avoids suggesting cycles)
                        result.Add(symbol.Name, CompletionItemFactory.CreateSymbolCompletion(symbol));
                    }
                }
            }

            // add namespaces first
            AddSymbolCompletions(completions, model.Root.ImportedNamespaces.Values);

            // add the non-output declarations with valid identifiers
            AddSymbolCompletions(completions, model.Root.AllDeclarations.Where(decl => decl.NameSyntax.IsValid && !(decl is OutputSymbol)));

            // get names of functions that always require to be fully qualified due to clashes between namespaces
            var alwaysFullyQualifiedNames = model.Root.ImportedNamespaces
                                            .SelectMany(pair => pair.Value.Descendants.OfType <FunctionSymbol>())
                                            .GroupBy(func => func.Name, (name, functionSymbols) => (name, count: functionSymbols.Count()), LanguageConstants.IdentifierComparer)
                                            .Where(tuple => tuple.count > 1)
                                            .Select(tuple => tuple.name)
                                            .ToHashSet(LanguageConstants.IdentifierComparer);

            foreach (var @namespace in model.Root.ImportedNamespaces.Values)
            {
                foreach (var function in @namespace.Descendants.OfType <FunctionSymbol>())
                {
                    if (function.FunctionFlags.HasFlag(FunctionFlags.ParamDefaultsOnly) && !(enclosingDeclarationSymbol is ParameterSymbol))
                    {
                        // this function is only allowed in param defaults but the enclosing declaration is not bound to a parameter symbol
                        // therefore we should not suggesting this function as a viable completion
                        continue;
                    }

                    if (completions.ContainsKey(function.Name) || alwaysFullyQualifiedNames.Contains(function.Name))
                    {
                        // either there is a declaration with the same name as the function or the function is ambiguous between the imported namespaces
                        // either way the function must be fully qualified in the completion
                        var fullyQualifiedFunctionName = $"{@namespace.Name}.{function.Name}";
                        completions.Add(fullyQualifiedFunctionName, CompletionItemFactory.CreateSymbolCompletion(function, insertText: fullyQualifiedFunctionName));
                    }
                    else
                    {
                        // function does not have to be fully qualified
                        completions.Add(function.Name, CompletionItemFactory.CreateSymbolCompletion(function));
                    }
                }
            }

            return(completions.Values);
        }