コード例 #1
0
 private IEnumerable <CompletionItem> GetResourceOrModuleBodyCompletions(BicepCompletionContext context)
 {
     if (context.Kind.HasFlag(BicepCompletionContextKind.ResourceBody) || context.Kind.HasFlag(BicepCompletionContextKind.ModuleBody))
     {
         yield return(CreateObjectBodyCompletion(context.ReplacementRange));
     }
 }
コード例 #2
0
        private IEnumerable <CompletionItem> GetResourceOrModuleBodyCompletions(BicepCompletionContext context)
        {
            if (context.Kind.HasFlag(BicepCompletionContextKind.ResourceBody) || context.Kind.HasFlag(BicepCompletionContextKind.ModuleBody))
            {
                yield return(CreateObjectBodyCompletion(context.ReplacementRange));

                // loops are always allowed in a resource/module
                yield return(CreateLoopCompletion(context.ReplacementRange, LanguageConstants.Object));
            }
        }
コード例 #3
0
        private IEnumerable <CompletionItem> GetDeclarationTypeCompletions(BicepCompletionContext context)
        {
            // local function
            IEnumerable <CompletionItem> GetPrimitiveTypeCompletions() =>
            LanguageConstants.DeclarationTypes.Values.Select(type => CreateTypeCompletion(type, context.ReplacementRange));

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

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

            return(Enumerable.Empty <CompletionItem>());
        }
コード例 #4
0
 private IEnumerable <CompletionItem> GetPrimitiveTypeCompletions(BicepCompletionContext completionContext) =>
 completionContext.Kind.HasFlag(BicepCompletionContextKind.Declaration) == false
         ? LanguageConstants.DeclarationTypes.Values.Select(CreateTypeCompletion)
         : Enumerable.Empty <CompletionItem>();
コード例 #5
0
 private IEnumerable <CompletionItem> GetTargetScopeCompletions(SemanticModel model, BicepCompletionContext context)
 {
     return(context.Kind.HasFlag(BicepCompletionContextKind.TargetScope) && context.TargetScope is {} targetScope
         ? GetValueCompletionsForType(model.GetDeclaredType(targetScope), context.ReplacementRange)
         : Enumerable.Empty <CompletionItem>());
 }
コード例 #6
0
        private IEnumerable <CompletionItem> GetSymbolCompletions(SemanticModel model, BicepCompletionContext context)
        {
            if (!context.Kind.HasFlag(BicepCompletionContextKind.Expression))
            {
                return(Enumerable.Empty <CompletionItem>());
            }

            if (context.Property != null && model.GetDeclaredTypeAssignment(context.Property)?.Flags == DeclaredTypeFlags.Constant)
            {
                // the enclosing property's declared type is supposed to be a constant value
                // the constant flag comes from TypeProperty constant flag, so nothing else can really alter it except for another property
                // (in other words constant flag inherits down into the expression tree of the property value)
                return(Enumerable.Empty <CompletionItem>());
            }

            // when we're inside an expression that is inside a property that expects a compile-time constant value,
            // we should not be emitting accessible symbol completions
            return(GetAccessibleSymbolCompletions(model, context));
        }
コード例 #7
0
        private IEnumerable <CompletionItem> GetDeclarationCompletions(BicepCompletionContext context)
        {
            if (context.Kind.HasFlag(BicepCompletionContextKind.TopLevelDeclarationStart))
            {
                yield return(CreateKeywordCompletion(LanguageConstants.ParameterKeyword, "Parameter keyword", context.ReplacementRange));

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                yield return(CreateContextualSnippetCompletion(LanguageConstants.ModuleKeyword, "Module declaration", @"module ${1:Identifier} '${2:Path}' = {
  name: $3
  $0
}", context.ReplacementRange));

                yield return(CreateKeywordCompletion(LanguageConstants.TargetScopeKeyword, "Target Scope keyword", context.ReplacementRange));
            }

            if (context.Kind.HasFlag(BicepCompletionContextKind.NestedResourceDeclarationStart))
            {
                yield return(CreateKeywordCompletion(LanguageConstants.ResourceKeyword, "Resource keyword", context.ReplacementRange));

                // leaving out the API version on this, because we expect its more common to inherit from the containing resource.
                yield return(CreateContextualSnippetCompletion(LanguageConstants.ResourceKeyword, "Nested resource with defaults", @"resource ${1:Identifier} '${2:Type}' = {
  name: $3
  properties: {
    $0
  }
}", context.ReplacementRange, insertTextMode: InsertTextMode.AdjustIndentation));

                yield return(CreateContextualSnippetCompletion(LanguageConstants.ResourceKeyword, "Nested resource without defaults", @"resource ${1:Identifier} '${2:Type}' = {
  name: $3
  $0
}
", context.ReplacementRange, insertTextMode: InsertTextMode.AdjustIndentation));
            }
        }
コード例 #8
0
        private IEnumerable <CompletionItem> GetSymbolCompletions(SemanticModel model, BicepCompletionContext context)
        {
            if (context.Kind != BicepCompletionContextKind.None)
            {
                return(Enumerable.Empty <CompletionItem>());
            }

            // when we're inside an expression that is inside a property that expects a compile-time constant value,
            // we should not be emitting accessible symbol completions
            return(GetAccessibleSymbolCompletions(model, context));
        }
コード例 #9
0
        private IEnumerable <CompletionItem> GetArrayIndexCompletions(Compilation compilation, BicepCompletionContext context)
        {
            if (!context.Kind.HasFlag(BicepCompletionContextKind.ArrayIndex) || context.ArrayAccess == null)
            {
                return(Enumerable.Empty <CompletionItem>());
            }

            var declaredType = compilation.GetEntrypointSemanticModel().GetDeclaredType(context.ArrayAccess.BaseExpression);

            return(GetProperties(declaredType)
                   .Where(p => !p.Flags.HasFlag(TypePropertyFlags.WriteOnly))
                   .Select(p => CreatePropertyIndexCompletion(p, context.ReplacementRange, CompletionPriority.High)));
        }
コード例 #10
0
        public IEnumerable <CompletionItem> GetFilteredCompletions(Compilation compilation, BicepCompletionContext context)
        {
            var model = compilation.GetEntrypointSemanticModel();

            return(GetDeclarationCompletions(context)
                   .Concat(GetSymbolCompletions(model, context))
                   .Concat(GetDeclarationTypeCompletions(context))
                   .Concat(GetObjectPropertyNameCompletions(model, context))
                   .Concat(GetMemberAccessCompletions(compilation, context))
                   .Concat(GetArrayIndexCompletions(compilation, context))
                   .Concat(GetPropertyValueCompletions(model, context))
                   .Concat(GetArrayItemCompletions(model, context))
                   .Concat(GetResourceTypeCompletions(model, context))
                   .Concat(GetResourceOrModuleBodyCompletions(context))
                   .Concat(GetTargetScopeCompletions(model, context)));
        }
コード例 #11
0
        private IEnumerable <CompletionItem> GetResourceTypeCompletions(SemanticModel model, BicepCompletionContext context)
        {
            if (!context.Kind.HasFlag(BicepCompletionContextKind.ResourceType))
            {
                return(Enumerable.Empty <CompletionItem>());
            }

            // we need to ensure that Microsoft.Compute/virtualMachines@whatever comes before Microsoft.Compute/virtualMachines/extensions@whatever
            // similarly, newest api versions should be shown first
            return(model.Compilation.ResourceTypeProvider.GetAvailableTypes()
                   .OrderBy(rt => rt.FullyQualifiedType, StringComparer.OrdinalIgnoreCase)
                   .ThenByDescending(rt => rt.ApiVersion)
                   .Select((reference, index) => CreateResourceTypeCompletion(reference, index, context.ReplacementRange))
                   .ToList());
        }
コード例 #12
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) && !string.Equals(symbol.Name, enclosingDeclarationSymbol?.Name, LanguageConstants.IdentifierComparison))
                    {
                        // the symbol satisfies the following conditions:
                        // - we have not added a symbol with the same name (avoids duplicate completions)
                        // - the symbol is different than the enclosing declaration (avoids suggesting cycles)
                        // - the symbol name is different than the name of the enclosing declaration (avoids suggesting a duplicate identifier)
                        result.Add(symbol.Name, CreateSymbolCompletion(symbol, context.ReplacementRange));
                    }
                }
            }

            // 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.Type.MethodResolver.GetKnownFunctions().Values)
                                            .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.Type.MethodResolver.GetKnownFunctions().Values)
                {
                    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, CreateSymbolCompletion(function, context.ReplacementRange, insertText: fullyQualifiedFunctionName));
                    }
                    else
                    {
                        // function does not have to be fully qualified
                        completions.Add(function.Name, CreateSymbolCompletion(function, context.ReplacementRange));
                    }
                }
            }

            return(completions.Values);
        }
コード例 #13
0
 private IEnumerable <CompletionItem> GetSymbolCompletions(SemanticModel model, BicepCompletionContext completionContext) =>
 completionContext.Kind.HasFlag(BicepCompletionContextKind.Declaration) == false
         ? GetAccessibleSymbols(model).Select(sym => sym.ToCompletionItem())
         : Enumerable.Empty <CompletionItem>();
コード例 #14
0
 public IEnumerable <CompletionItem> GetFilteredCompletions(SemanticModel model, BicepCompletionContext context)
 {
     return(GetDeclarationCompletions(context)
            .Concat(GetSymbolCompletions(model, context))
            .Concat(GetPrimitiveTypeCompletions(context)));
 }
コード例 #15
0
        private IEnumerable <CompletionItem> GetModulePathCompletions(SemanticModel model, BicepCompletionContext context)
        {
            if (!context.Kind.HasFlag(BicepCompletionContextKind.ModulePath))
            {
                return(Enumerable.Empty <CompletionItem>());
            }

            // To provide intellisense before the quotes are typed
            if (context.EnclosingDeclaration is not ModuleDeclarationSyntax declarationSyntax ||
                declarationSyntax.Path is not StringSyntax stringSyntax ||
                stringSyntax.TryGetLiteralValue() is not string entered)
            {
                entered = "";
            }

            // These should only fail if we're not able to resolve cwd path or the entered string
            if (FileResolver.TryResolveModulePath(model.SyntaxTree.FileUri, ".") is not {
            } cwdUri ||
                FileResolver.TryResolveModulePath(cwdUri, entered) is not {
            } query)
            {
                return(Enumerable.Empty <CompletionItem>());
            }

            var files = Enumerable.Empty <Uri>();
            var dirs  = Enumerable.Empty <Uri>();


            // technically bicep files do not have to follow the bicep extension, so
            // we are not enforcing *.bicep get files command
            if (FileResolver.TryDirExists(query))
            {
                files = FileResolver.GetFiles(query, string.Empty);
                dirs  = FileResolver.GetDirectories(query, string.Empty);
            }
            else if (FileResolver.TryResolveModulePath(query, ".") is {} queryParent)
            {
                files = FileResolver.GetFiles(queryParent, "");
                dirs  = FileResolver.GetDirectories(queryParent, "");
            }
            // "./" will not be preserved when making relative Uris. We have to go and manually add it.
            // Prioritize .bicep files higher than other files.
            var fileItems = files
                            .Where(file => file != model.SyntaxTree.FileUri)
                            .Where(file => file.Segments.Last().EndsWith(LanguageServerConstants.LanguageFileExtension))
                            .Select(file => CreateModulePathCompletion(
                                        file.Segments.Last(),
                                        (entered.StartsWith("./") ? "./" : "") + cwdUri.MakeRelativeUri(file).ToString(),
                                        context.ReplacementRange,
                                        CompletionItemKind.File,
                                        file.Segments.Last().EndsWith(LanguageServerConstants.LanguageId) ? CompletionPriority.High : CompletionPriority.Medium))
                            .ToList();

            var dirItems = dirs
                           .Select(dir => CreateModulePathCompletion(
                                       dir.Segments.Last(),
                                       (entered.StartsWith("./") ? "./" : "") + cwdUri.MakeRelativeUri(dir).ToString(),
                                       context.ReplacementRange,
                                       CompletionItemKind.Folder,
                                       CompletionPriority.Medium)
                                   .WithCommand(new Command {
                Name = EditorCommands.RequestCompletions
            }))
                           .ToList();

            return(fileItems.Concat(dirItems));
        }
コード例 #16
0
 private IEnumerable <CompletionItem> GetSymbolCompletions(SemanticModel model, BicepCompletionContext completionContext) =>
 completionContext.Kind == BicepCompletionContextKind.None
         ? GetAccessibleSymbols(model).Select(sym => sym.ToCompletionItem())
         : Enumerable.Empty <CompletionItem>();
コード例 #17
0
        private static IEnumerable <CompletionItem> GetAccessibleSymbolCompletions(SemanticModel model, BicepCompletionContext context)
        {
            // maps insert text to the completion item
            var completions = new Dictionary <string, CompletionItem>();

            var declaredNames = new HashSet <string>();

            var accessibleDecoratorFunctionsCache = new Dictionary <NamespaceType, IEnumerable <FunctionSymbol> >();

            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))
                    {
                        // the symbol satisfies the following conditions:
                        // - we have not added a symbol with the same name (avoids duplicate completions)
                        // - the symbol is different than the enclosing declaration (avoids suggesting cycles)
                        // - the symbol name is different than the name of the enclosing declaration (avoids suggesting a duplicate identifier)
                        result.Add(symbol.Name, CreateSymbolCompletion(symbol, context.ReplacementRange));
                    }
                }
            }

            // local function
            IEnumerable <FunctionSymbol> GetAccessibleDecoratorFunctionsWithCache(NamespaceType namespaceType)
            {
                if (accessibleDecoratorFunctionsCache.TryGetValue(namespaceType, out var result))
                {
                    return(result);
                }

                result = GetAccessibleDecoratorFunctions(namespaceType, enclosingDeclarationSymbol);
                accessibleDecoratorFunctionsCache.Add(namespaceType, result);

                return(result);
            }

            if (!context.Kind.HasFlag(BicepCompletionContextKind.DecoratorName))
            {
                // add namespaces first
                AddSymbolCompletions(completions, model.Root.ImportedNamespaces.Values);

                // add accessible symbols from innermost scope and then move to outer scopes
                // reverse loop iteration
                for (int depth = context.ActiveScopes.Length - 1; depth >= 0; depth--)
                {
                    // add the non-output declarations with valid identifiers at current scope
                    var currentScope = context.ActiveScopes[depth];
                    AddSymbolCompletions(completions, currentScope.AllDeclarations.Where(decl => decl.NameSyntax.IsValid && !(decl is OutputSymbol)));
                }
            }
            else
            {
                // Only add the namespaces that contain accessible decorator function symbols.
                AddSymbolCompletions(completions, model.Root.ImportedNamespaces.Values.Where(
                                         @namespace => GetAccessibleDecoratorFunctionsWithCache(@namespace.Type).Any()));

                // Record the names of the non-output declarations which will be used to check name clashes later.
                declaredNames.UnionWith(model.Root.AllDeclarations.Where(decl => decl.NameSyntax.IsValid && decl is not OutputSymbol).Select(decl => decl.Name));
            }

            // get names of functions that always require to be fully qualified due to clashes between namespaces
            var alwaysFullyQualifiedNames = model.Root.ImportedNamespaces
                                            .SelectMany(pair => context.Kind.HasFlag(BicepCompletionContextKind.DecoratorName)
                    ? GetAccessibleDecoratorFunctionsWithCache(pair.Value.Type)
                    : pair.Value.Type.MethodResolver.GetKnownFunctions().Values)
                                            .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)
            {
                var functionSymbols = context.Kind.HasFlag(BicepCompletionContextKind.DecoratorName)
                    ? GetAccessibleDecoratorFunctionsWithCache(@namespace.Type)
                    : @namespace.Type.MethodResolver.GetKnownFunctions().Values;

                foreach (var function in functionSymbols)
                {
                    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) || declaredNames.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, CreateSymbolCompletion(function, context.ReplacementRange, insertText: fullyQualifiedFunctionName));
                    }
                    else
                    {
                        // function does not have to be fully qualified
                        completions.Add(function.Name, CreateSymbolCompletion(function, context.ReplacementRange));
                    }
                }
            }

            return(completions.Values);
        }
コード例 #18
0
 public IEnumerable <CompletionItem> GetFilteredCompletions(SemanticModel model, BicepCompletionContext context)
 {
     return(GetDeclarationCompletions(context)
            .Concat(GetSymbolCompletions(model, context))
            .Concat(GetDeclarationTypeCompletions(context))
            .Concat(GetObjectPropertyNameCompletions(model, context))
            .Concat(GetPropertyValueCompletions(model, context))
            .Concat(GetArrayItemCompletions(model, context)));
 }
コード例 #19
0
        private IEnumerable <CompletionItem> GetPropertyValueCompletions(SemanticModel model, BicepCompletionContext context)
        {
            if (!context.Kind.HasFlag(BicepCompletionContextKind.PropertyValue))
            {
                return(Enumerable.Empty <CompletionItem>());
            }

            var declaredTypeAssignment = GetDeclaredTypeAssignment(model, context.Property);

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

            return(GetValueCompletions(model, context, declaredTypeAssignment.Reference.Type, declaredTypeAssignment.Flags));
        }
コード例 #20
0
        private IEnumerable <CompletionItem> GetMemberAccessCompletions(Compilation compilation, BicepCompletionContext context)
        {
            if (!context.Kind.HasFlag(BicepCompletionContextKind.MemberAccess) || context.PropertyAccess == null)
            {
                return(Enumerable.Empty <CompletionItem>());
            }

            var declaredType = compilation.GetEntrypointSemanticModel().GetDeclaredType(context.PropertyAccess.BaseExpression);

            return(GetProperties(declaredType)
                   .Where(p => !p.Flags.HasFlag(TypePropertyFlags.WriteOnly))
                   .Select(p => CreatePropertyAccessCompletion(p, compilation.SyntaxTreeGrouping.EntryPoint, context.PropertyAccess, context.ReplacementRange))
                   .Concat(GetMethods(declaredType)
                           .Select(m => CreateSymbolCompletion(m, context.ReplacementRange))));
        }
コード例 #21
0
        private IEnumerable <CompletionItem> GetArrayItemCompletions(SemanticModel model, BicepCompletionContext context)
        {
            if (!context.Kind.HasFlag(BicepCompletionContextKind.ArrayItem))
            {
                return(Enumerable.Empty <CompletionItem>());
            }

            var declaredTypeAssignment = GetDeclaredTypeAssignment(model, context.Array);

            if (declaredTypeAssignment == null || !(declaredTypeAssignment.Reference.Type is ArrayType arrayType))
            {
                return(Enumerable.Empty <CompletionItem>());
            }

            return(GetValueCompletions(model, context, arrayType.Item.Type, declaredTypeAssignment.Flags));
        }
コード例 #22
0
        private IEnumerable <CompletionItem> GetObjectPropertyNameCompletions(SemanticModel model, BicepCompletionContext context)
        {
            if (context.Kind.HasFlag(BicepCompletionContextKind.ObjectPropertyName) == 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) && specifiedPropertyNames.Contains(p.Name) == false)
                   .Select(p => CreatePropertyNameCompletion(p, context.ReplacementRange)));
        }
コード例 #23
0
        private static IEnumerable <CompletionItem> GetValueCompletions(SemanticModel model, BicepCompletionContext context, TypeSymbol type, DeclaredTypeFlags flags)
        {
            var completions = GetValueCompletionsForType(type);

            if (flags != DeclaredTypeFlags.Constant)
            {
                completions = completions.Concat(GetAccessibleSymbolCompletions(model, context));
            }

            return(completions);
        }
コード例 #24
0
        private IEnumerable <CompletionItem> GetDeclarationCompletions(BicepCompletionContext context)
        {
            if (context.Kind.HasFlag(BicepCompletionContextKind.DeclarationStart))
            {
                yield return(CreateKeywordCompletion(LanguageConstants.ParameterKeyword, "Parameter keyword", context.ReplacementRange));

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                yield return(CreateContextualSnippetCompletion(LanguageConstants.ModuleKeyword, "Module declaration", @"module ${1:Identifier} '${2:Path}' = {
  name: $3
  $0
}", context.ReplacementRange));

                yield return(CreateKeywordCompletion(LanguageConstants.TargetScopeKeyword, "Target Scope keyword", context.ReplacementRange));
            }
        }
コード例 #25
0
        private IEnumerable <CompletionItem> GetResourceTypeCompletions(SemanticModel model, BicepCompletionContext context)
        {
            if (!context.Kind.HasFlag(BicepCompletionContextKind.ResourceType))
            {
                return(Enumerable.Empty <CompletionItem>());
            }

            // For a nested resource, we want to filter the set of types.
            //
            // The strategy when *can't* filter - due to errors - to fallback to the main path and offer full completions
            // then once the user corrects whatever's cause the error, they will be told to simplify the type.
            if (context.EnclosingDeclaration is SyntaxBase &&
                model.Binder.GetNearestAncestor <ResourceDeclarationSyntax>(context.EnclosingDeclaration) is ResourceDeclarationSyntax parentSyntax &&
                model.GetSymbolInfo(parentSyntax) is ResourceSymbol parentSymbol &&
                parentSymbol.TryGetResourceTypeReference() is ResourceTypeReference parentTypeReference)
            {
                // This is more complex because we allow the API version to be omitted, so we want to make a list of unique values
                // for the FQT, and then create a "no version" completion + a completion for each version.
                var filtered = model.Compilation.ResourceTypeProvider.GetAvailableTypes()
                               .Where(rt => parentTypeReference.IsParentOf(rt))
                               .ToLookup(rt => rt.FullyQualifiedType);

                var index = 0;
                var items = new List <CompletionItem>();
                foreach (var group in filtered.OrderBy(group => group.Key, StringComparer.OrdinalIgnoreCase))
                {
                    // Doesn't matter which one of the group we take, we're leaving out the version.
                    items.Add(CreateResourceTypeSegmentCompletion(group.First(), index++, context.ReplacementRange, includeApiVersion: false, displayApiVersion: parentTypeReference.ApiVersion));

                    foreach (var resourceType in group.OrderByDescending(rt => rt.ApiVersion, ApiVersionComparer.Instance))
                    {
                        items.Add(CreateResourceTypeSegmentCompletion(resourceType, index++, context.ReplacementRange, includeApiVersion: true, displayApiVersion: resourceType.ApiVersion));
                    }
                }

                return(items);
            }
            // we need to ensure that Microsoft.Compute/virtualMachines@whatever comes before Microsoft.Compute/virtualMachines/extensions@whatever
            // similarly, newest api versions should be shown first
            return(model.Compilation.ResourceTypeProvider.GetAvailableTypes()
                   .OrderBy(rt => rt.FullyQualifiedType, StringComparer.OrdinalIgnoreCase)
                   .ThenByDescending(rt => rt.ApiVersion, ApiVersionComparer.Instance)
                   .Select((reference, index) => CreateResourceTypeCompletion(reference, index, context.ReplacementRange))
                   .ToList());
        }