private static BicepCompletionContextKind GetDeclarationTypeFlags()

in src/Bicep.LangServer/Completions/BicepCompletionContext.cs [343:433]


        private static BicepCompletionContextKind GetDeclarationTypeFlags(IList<SyntaxBase> matchingNodes, int offset)
        {
            // local function
            bool CheckTypeIsExpected(SyntaxBase name, SyntaxBase type) => name.Span.Length > 0 && offset > name.GetEndPosition() && offset <= type.Span.Position;

            bool CheckParameterResourceTypeIsExpected(ResourceTypeSyntax type) =>
                // This handles the case where we have the resource keyword but no type-string for a parameter.
                // Must be inside the type
                type.Span.Length > 0 &&
                offset >= type.Keyword.GetEndPosition() &&
                (type.Type is null || type.Type is SkippedTriviaSyntax);

            bool CheckOutputResourceTypeIsExpected(OutputDeclarationSyntax output) =>
                // This handles the case where we have the resource keyword but no type-string for an output.
                // Must be after the type (`resource` is valid for type) and
                // Before the `=` if `=` is present
                output.Type is ResourceTypeSyntax type &&
                offset >= type.Keyword.GetEndPosition() &&
                (type.Type is null || type.Type is SkippedTriviaSyntax) &&
                (output.Assignment is SkippedTriviaSyntax || (output.Assignment is Token assignment && offset <= assignment.GetPosition()));

            if (SyntaxMatcher.IsTailMatch<ParameterDeclarationSyntax>(matchingNodes, parameter => CheckTypeIsExpected(parameter.Name, parameter.Type)) ||
                SyntaxMatcher.IsTailMatch<ParameterDeclarationSyntax, TypeVariableAccessSyntax, IdentifierSyntax, Token>(matchingNodes, (_, _, _, token) => token.Type == TokenType.Identifier))
            {
                // the most specific matching node is a parameter declaration
                // the declaration syntax is "param <identifier> <type> ..."
                // the cursor position is on the type if we have an identifier (non-zero length span) and the offset matches the type position
                // OR
                // we are in a token that is inside a TypeVariableAccessSyntax node, which is inside a parameter node
                return BicepCompletionContextKind.ParameterType;
            }

            if (SyntaxMatcher.IsTailMatch<TypeDeclarationSyntax>(matchingNodes, typeDeclaration => typeDeclaration.Assignment is not SkippedTriviaSyntax && offset > typeDeclaration.Assignment.GetEndPosition() && offset <= typeDeclaration.Value.Span.Position) ||
                SyntaxMatcher.IsTailMatch<TypeDeclarationSyntax, TypeVariableAccessSyntax, IdentifierSyntax, Token>(matchingNodes, (_, _, _, token) => token.Type == TokenType.Identifier))
            {
                // the most specific matching node is a type declaration
                // the declaration syntax is "type <identifier> = <type>"
                // the cursor position is on the type if we have an identifier (non-zero length span) and the offset matches the type position
                // OR
                // we are in a token that is inside a TypeVariableAccessSyntax node, which is inside a type declaration node
                return BicepCompletionContextKind.TypeDeclarationValue;
            }

            if (SyntaxMatcher.IsTailMatch<ParameterDeclarationSyntax, ResourceTypeSyntax>(matchingNodes, (parameter, type) => CheckParameterResourceTypeIsExpected(type)) ||
                SyntaxMatcher.IsTailMatch<ParameterDeclarationSyntax, ResourceTypeSyntax, StringSyntax, Token>(matchingNodes, (_, _, _, token) => token.Type == TokenType.StringComplete))
            {
                // the most specific matching node is a parameter declaration with the resource keyword
                // the declaration syntax is "param <identifier> resource ..."
                // the cursor position is on the resource type if we have the resource keyword but nothing else
                // OR
                // we are in a token that is inside a ResourceTypeSyntax node, which is inside a parameter node
                return BicepCompletionContextKind.ResourceType;
            }

            if (SyntaxMatcher.IsTailMatch<OutputDeclarationSyntax>(matchingNodes, output => CheckTypeIsExpected(output.Name, output.Type)) ||
                SyntaxMatcher.IsTailMatch<OutputDeclarationSyntax, TypeVariableAccessSyntax, IdentifierSyntax, Token>(matchingNodes, (_, _, _, token) => token.Type == TokenType.Identifier))
            {
                // the most specific matching node is an output declaration
                // the declaration syntax is "output <identifier> <type> ..."
                // the cursor position is on the type if we have an identifier (non-zero length span) and the offset matches the type position
                // OR
                // we are in a token that is inside a TypeVariableAccessSyntax node, which is inside an output node
                return BicepCompletionContextKind.OutputType;
            }

            // NOTE: this logic is different between parameters and outputs because the resource type is optional for outputs.
            if (SyntaxMatcher.IsTailMatch<OutputDeclarationSyntax>(matchingNodes, output => CheckOutputResourceTypeIsExpected(output)) ||
                SyntaxMatcher.IsTailMatch<OutputDeclarationSyntax, ResourceTypeSyntax, StringSyntax, Token>(matchingNodes, (_, _, _, token) => token.Type == TokenType.StringComplete))
            {
                // the most specific matching node is an output declaration with the resource keyword
                // the declaration syntax is "output <identifier> resource ..."
                // the cursor position is on the resource type if we have the resource keyword but nothing else
                // OR
                // we are in a token that is inside a ResourceTypeSyntax node, which is inside an output node
                return BicepCompletionContextKind.ResourceType;
            }

            if (IsResourceTypeContext(matchingNodes, offset))
            {
                // the most specific matching node is a resource declaration
                // the declaration syntax is "resource <identifier> '<type>' ..."
                // the cursor position is on the type if we have an identifier (non-zero length span) and the offset matches the type position
                // OR
                // we are in a token that is inside a StringSyntax node, which is inside a resource declaration
                // OR
                // we have an identifier in the place of a type in a resoure (this allows us to show completions when user just types virtualMachines instead of 'virtualMachines')
                return BicepCompletionContextKind.ResourceType;
            }

            return BicepCompletionContextKind.None;
        }