Skip to content

Commit

Permalink
Add completion for nested resources
Browse files Browse the repository at this point in the history
  • Loading branch information
rynowak committed Mar 4, 2021
1 parent 81830d9 commit 51c32ae
Show file tree
Hide file tree
Showing 7 changed files with 276 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,56 @@
":"
]
},
{
"label": "resource",
"kind": "keyword",
"detail": "Resource keyword",
"deprecated": false,
"preselect": false,
"sortText": "2_resource",
"insertTextFormat": "plainText",
"insertTextMode": "asIs",
"textEdit": {
"range": {},
"newText": "resource"
}
},
{
"label": "resource",
"kind": "snippet",
"detail": "Nested resource with defaults",
"documentation": {
"kind": "markdown",
"value": "```bicep\nresource Identifier 'Type' = {\n name: \n properties: {\n \n }\n}\n```"
},
"deprecated": false,
"preselect": false,
"sortText": "2_resource",
"insertTextFormat": "snippet",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "resource ${1:Identifier} '${2:Type}' = {\n name: $3\n properties: {\n $0\n }\n}"
}
},
{
"label": "resource",
"kind": "snippet",
"detail": "Nested resource without defaults",
"documentation": {
"kind": "markdown",
"value": "```bicep\nresource Identifier 'Type' = {\n name: \n \n}\n\n```"
},
"deprecated": false,
"preselect": false,
"sortText": "2_resource",
"insertTextFormat": "snippet",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "resource ${1:Identifier} '${2:Type}' = {\n name: $3\n $0\n}\n"
}
},
{
"label": "tags",
"kind": "property",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,55 @@
"commitCharacters": [
":"
]
},
{
"label": "resource",
"kind": "keyword",
"detail": "Resource keyword",
"deprecated": false,
"preselect": false,
"sortText": "2_resource",
"insertTextFormat": "plainText",
"insertTextMode": "asIs",
"textEdit": {
"range": {},
"newText": "resource"
}
},
{
"label": "resource",
"kind": "snippet",
"detail": "Nested resource with defaults",
"documentation": {
"kind": "markdown",
"value": "```bicep\nresource Identifier 'Type' = {\n name: \n properties: {\n \n }\n}\n```"
},
"deprecated": false,
"preselect": false,
"sortText": "2_resource",
"insertTextFormat": "snippet",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "resource ${1:Identifier} '${2:Type}' = {\n name: $3\n properties: {\n $0\n }\n}"
}
},
{
"label": "resource",
"kind": "snippet",
"detail": "Nested resource without defaults",
"documentation": {
"kind": "markdown",
"value": "```bicep\nresource Identifier 'Type' = {\n name: \n \n}\n\n```"
},
"deprecated": false,
"preselect": false,
"sortText": "2_resource",
"insertTextFormat": "snippet",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "resource ${1:Identifier} '${2:Type}' = {\n name: $3\n $0\n}\n"
}
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,56 @@
":"
]
},
{
"label": "resource",
"kind": "keyword",
"detail": "Resource keyword",
"deprecated": false,
"preselect": false,
"sortText": "2_resource",
"insertTextFormat": "plainText",
"insertTextMode": "asIs",
"textEdit": {
"range": {},
"newText": "resource"
}
},
{
"label": "resource",
"kind": "snippet",
"detail": "Nested resource with defaults",
"documentation": {
"kind": "markdown",
"value": "```bicep\nresource Identifier 'Type' = {\n name: \n properties: {\n \n }\n}\n```"
},
"deprecated": false,
"preselect": false,
"sortText": "2_resource",
"insertTextFormat": "snippet",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "resource ${1:Identifier} '${2:Type}' = {\n name: $3\n properties: {\n $0\n }\n}"
}
},
{
"label": "resource",
"kind": "snippet",
"detail": "Nested resource without defaults",
"documentation": {
"kind": "markdown",
"value": "```bicep\nresource Identifier 'Type' = {\n name: \n \n}\n\n```"
},
"deprecated": false,
"preselect": false,
"sortText": "2_resource",
"insertTextFormat": "snippet",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "resource ${1:Identifier} '${2:Type}' = {\n name: $3\n $0\n}\n"
}
},
{
"label": "sku",
"kind": "property",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,56 @@
":"
]
},
{
"label": "resource",
"kind": "keyword",
"detail": "Resource keyword",
"deprecated": false,
"preselect": false,
"sortText": "2_resource",
"insertTextFormat": "plainText",
"insertTextMode": "asIs",
"textEdit": {
"range": {},
"newText": "resource"
}
},
{
"label": "resource",
"kind": "snippet",
"detail": "Nested resource with defaults",
"documentation": {
"kind": "markdown",
"value": "```bicep\nresource Identifier 'Type' = {\n name: \n properties: {\n \n }\n}\n```"
},
"deprecated": false,
"preselect": false,
"sortText": "2_resource",
"insertTextFormat": "snippet",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "resource ${1:Identifier} '${2:Type}' = {\n name: $3\n properties: {\n $0\n }\n}"
}
},
{
"label": "resource",
"kind": "snippet",
"detail": "Nested resource without defaults",
"documentation": {
"kind": "markdown",
"value": "```bicep\nresource Identifier 'Type' = {\n name: \n \n}\n\n```"
},
"deprecated": false,
"preselect": false,
"sortText": "2_resource",
"insertTextFormat": "snippet",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "resource ${1:Identifier} '${2:Type}' = {\n name: $3\n $0\n}\n"
}
},
{
"label": "sku",
"kind": "property",
Expand Down
54 changes: 53 additions & 1 deletion src/Bicep.LangServer/Completions/BicepCompletionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,12 @@ public static BicepCompletionContext Create(Compilation compilation, int offset)
var activeScopes = ActiveScopesVisitor.GetActiveScopes(compilation.GetEntrypointSemanticModel().Root, offset);

var kind = ConvertFlag(IsTopLevelDeclarationStartContext(matchingNodes, offset), BicepCompletionContextKind.TopLevelDeclarationStart) |
ConvertFlag(IsNestedResourceStartContext(matchingNodes, topLevelDeclarationInfo, objectInfo, offset), BicepCompletionContextKind.NestedResourceDeclarationStart) |
GetDeclarationTypeFlags(matchingNodes, offset) |
ConvertFlag(IsObjectPropertyNameContext(matchingNodes, objectInfo), BicepCompletionContextKind.ObjectPropertyName) |
ConvertFlag(IsMemberAccessContext(matchingNodes, propertyAccessInfo, offset), BicepCompletionContextKind.MemberAccess) |
ConvertFlag(IsResourceAccessContext(matchingNodes, resourceAccessInfo, offset), BicepCompletionContextKind.ResourceAccess) |
ConvertFlag(IsArrayIndexContext(matchingNodes,arrayAccessInfo), BicepCompletionContextKind.ArrayIndex | BicepCompletionContextKind.Expression) |
ConvertFlag(IsArrayIndexContext(matchingNodes, arrayAccessInfo), BicepCompletionContextKind.ArrayIndex | BicepCompletionContextKind.Expression) |
ConvertFlag(IsPropertyValueContext(matchingNodes, propertyInfo), BicepCompletionContextKind.PropertyValue | BicepCompletionContextKind.Expression) |
ConvertFlag(IsArrayItemContext(matchingNodes, arrayInfo), BicepCompletionContextKind.ArrayItem | BicepCompletionContextKind.Expression) |
ConvertFlag(IsResourceBodyContext(matchingNodes, offset), BicepCompletionContextKind.ResourceBody) |
Expand Down Expand Up @@ -258,6 +259,57 @@ private static bool IsTopLevelDeclarationStartContext(List<SyntaxBase> matchingN
return false;
}

private static bool IsNestedResourceStartContext(List<SyntaxBase> matchingNodes, (SyntaxBase? node, int index) topLevelDeclarationInfo, (ObjectSyntax? node, int index) objectInfo, int offset)
{
// A nested resource must be at the top level of a resource declaration (not nested within another level of object).
if (objectInfo.node == null)
{
// none of the matching nodes are ObjectSyntax,
// so we cannot possibly be in a position to begin an object property
return false;
}

if (topLevelDeclarationInfo.node == null || topLevelDeclarationInfo.node is not ResourceDeclarationSyntax resourceDeclarationSyntax)
{
// not inside of a resource, top level declarations are handled separately.
return false;
}

if (!object.ReferenceEquals(resourceDeclarationSyntax.TryGetBody(), objectInfo.node))
{
// we're inside of an object, but it's not the body of a resource.
return false;
}

switch (matchingNodes[^1])
{
case ObjectSyntax _:
// we are somewhere in the trivia portion of the object node (trivia span is not included in the token span)
// which is why the last node in the list of matching nodes is not a Token.
return true;

case Token token:
int nodeCount = matchingNodes.Count - objectInfo.index;

switch (nodeCount)
{
case 2 when token.Type == TokenType.NewLine:
return true;

case 4 when matchingNodes[^2] is IdentifierSyntax identifier && matchingNodes[^3] is ObjectPropertySyntax property && ReferenceEquals(property.Key, identifier):
// we are in a partial or full property name
return true;

case 4 when matchingNodes[^2] is SkippedTriviaSyntax skipped && matchingNodes[^3] is ObjectPropertySyntax property && ReferenceEquals(property.Key, skipped):
return true;
}

break;
}

return false;
}

private static bool IsMemberAccessContext(List<SyntaxBase> matchingNodes, (PropertyAccessSyntax? node, int index) propertyAccessInfo, int offset)
{
return propertyAccessInfo.node != null &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,9 @@ public enum BicepCompletionContextKind
/// </summary>
DecoratorName = 1 << 15,


/// <summary>
/// The current location could be the start of a nested resource declaration.
/// </summary>
NestedResourceDeclarationStart = 1 << 16,
}
}
19 changes: 19 additions & 0 deletions src/Bicep.LangServer/Completions/BicepCompletionProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,25 @@ private IEnumerable<CompletionItem> GetDeclarationCompletions(BicepCompletionCon

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

private IEnumerable<CompletionItem> GetTargetScopeCompletions(SemanticModel model, BicepCompletionContext context)
Expand Down

0 comments on commit 51c32ae

Please sign in to comment.