Skip to content

Commit

Permalink
Add "required-properties" snippet to modules (#2658)
Browse files Browse the repository at this point in the history
* Add required-properties snippet to module

* Removed empty line

* Fix TypeSymbol issue and update baseline for tests
  • Loading branch information
bhsubra authored May 14, 2021
1 parent b8362b1 commit c8b397d
Show file tree
Hide file tree
Showing 14 changed files with 285 additions and 16 deletions.
8 changes: 6 additions & 2 deletions src/Bicep.Core.Samples/Files/Completions/moduleObject.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,15 @@
},
{
"label": "{}",
"kind": "value",
"kind": "snippet",
"detail": "{}",
"documentation": {
"kind": "markdown",
"value": "```bicep\n{\n\t\n}\n```"
},
"deprecated": false,
"preselect": true,
"sortText": "1_{}",
"sortText": "2_{}",
"insertTextFormat": "snippet",
"insertTextMode": "adjustIndentation",
"textEdit": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
[
{
"label": "for",
"kind": "snippet",
"detail": "for",
"documentation": {
"kind": "markdown",
"value": "```bicep\n[for item in list: {\n\t\n}]\n```"
},
"deprecated": false,
"preselect": false,
"sortText": "1_for",
"insertTextFormat": "snippet",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "[for ${2:item} in ${1:list}: {\n\t$0\n}]"
}
},
{
"label": "for-filtered",
"kind": "snippet",
"detail": "for-filtered",
"documentation": {
"kind": "markdown",
"value": "```bicep\n[for (item, index) in list: if (condition) {\n\t\n}]\n```"
},
"deprecated": false,
"preselect": false,
"sortText": "1_for-filtered",
"insertTextFormat": "snippet",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "[for (${2:item}, ${3:index}) in ${1:list}: if (${4:condition}) {\n\t$0\n}]"
}
},
{
"label": "for-indexed",
"kind": "snippet",
"detail": "for-indexed",
"documentation": {
"kind": "markdown",
"value": "```bicep\n[for (item, index) in list: {\n\t\n}]\n```"
},
"deprecated": false,
"preselect": false,
"sortText": "1_for-indexed",
"insertTextFormat": "snippet",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "[for (${2:item}, ${3:index}) in ${1:list}: {\n\t$0\n}]"
}
},
{
"label": "if",
"kind": "snippet",
"detail": "if",
"deprecated": false,
"preselect": false,
"sortText": "1_if",
"insertTextFormat": "snippet",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "if (${1:condition}) {\n\t$0\n}"
}
},
{
"label": "required-properties",
"kind": "snippet",
"detail": "Required properties",
"documentation": {
"kind": "markdown",
"value": "```bicep\n{\n\tname: \n\tparams: {\n\t\tarrayParam: \n\t\tobjParam: {\n\t\t}\n\t\tstringParamB: \n\t}\n\t\n}\n```"
},
"deprecated": false,
"preselect": true,
"sortText": "2_required-properties",
"insertTextFormat": "snippet",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "{\n\tname: $1\n\tparams: {\n\t\tarrayParam: $2\n\t\tobjParam: {\n\t\t}\n\t\tstringParamB: $3\n\t}\n\t$0\n}"
}
},
{
"label": "{}",
"kind": "snippet",
"detail": "{}",
"documentation": {
"kind": "markdown",
"value": "```bicep\n{\n\t\n}\n```"
},
"deprecated": false,
"preselect": true,
"sortText": "2_{}",
"insertTextFormat": "snippet",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "{\n\t$0\n}"
}
}
]
Original file line number Diff line number Diff line change
Expand Up @@ -1671,6 +1671,20 @@
"newText": "moduleWithNotAttachableDecorators"
}
},
{
"label": "moduleWithPath",
"kind": "module",
"detail": "moduleWithPath",
"deprecated": false,
"preselect": false,
"sortText": "2_moduleWithPath",
"insertTextFormat": "plainText",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "moduleWithPath"
}
},
{
"label": "moduleWithSelfCycle",
"kind": "module",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1671,6 +1671,20 @@
"newText": "moduleWithNotAttachableDecorators"
}
},
{
"label": "moduleWithPath",
"kind": "module",
"detail": "moduleWithPath",
"deprecated": false,
"preselect": false,
"sortText": "2_moduleWithPath",
"insertTextFormat": "plainText",
"insertTextMode": "adjustIndentation",
"textEdit": {
"range": {},
"newText": "moduleWithPath"
}
},
{
"label": "moduleWithSelfCycle",
"kind": "module",
Expand Down
3 changes: 3 additions & 0 deletions src/Bicep.Core.Samples/Files/InvalidModules_LF/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ module moduleWithoutPath = {

}

// #completionTest(41) -> moduleBodyCompletions
module moduleWithPath './moduleb.bicep' =

// missing identifier #completionTest(7) -> empty
module

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ module moduleWithoutPath = {
}
//@[0:1) [BCP007 (Error)] This declaration type is not recognized. Specify a parameter, variable, resource, or output declaration. |}|

// #completionTest(41) -> moduleBodyCompletions
module moduleWithPath './moduleb.bicep' =
//@[41:41) [BCP118 (Error)] Expected the "{" character, the "[" character, or the "if" keyword at this location. ||

// missing identifier #completionTest(7) -> empty
module
//@[7:7) [BCP096 (Error)] Expected a module identifier at this location. ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ module moduleWithoutPath = {

}

// #completionTest(41) -> moduleBodyCompletions
module moduleWithPath './moduleb.bicep' =

// missing identifier #completionTest(7) -> empty
module

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ module moduleWithoutPath = {

}

// #completionTest(41) -> moduleBodyCompletions
module moduleWithPath './moduleb.bicep' =
//@[7:21) Module moduleWithPath. Type: module. Declaration start char: 0, length: 41

// missing identifier #completionTest(7) -> empty
module
//@[7:7) Module <missing>. Type: error. Declaration start char: 0, length: 7
Expand Down
13 changes: 13 additions & 0 deletions src/Bicep.Core.Samples/Files/InvalidModules_LF/main.syntax.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,19 @@ module moduleWithoutPath = {
//@[0:1) RightBrace |}|
//@[1:3) NewLine |\n\n|

// #completionTest(41) -> moduleBodyCompletions
//@[47:48) NewLine |\n|
module moduleWithPath './moduleb.bicep' =
//@[0:41) ModuleDeclarationSyntax
//@[0:6) Identifier |module|
//@[7:21) IdentifierSyntax
//@[7:21) Identifier |moduleWithPath|
//@[22:39) StringSyntax
//@[22:39) StringComplete |'./moduleb.bicep'|
//@[40:41) Assignment |=|
//@[41:41) SkippedTriviaSyntax
//@[41:43) NewLine |\n\n|

// missing identifier #completionTest(7) -> empty
//@[49:50) NewLine |\n|
module
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,15 @@ module moduleWithoutPath = {
//@[0:1) RightBrace |}|
//@[1:3) NewLine |\n\n|

// #completionTest(41) -> moduleBodyCompletions
//@[47:48) NewLine |\n|
module moduleWithPath './moduleb.bicep' =
//@[0:6) Identifier |module|
//@[7:21) Identifier |moduleWithPath|
//@[22:39) StringComplete |'./moduleb.bicep'|
//@[40:41) Assignment |=|
//@[41:43) NewLine |\n\n|

// missing identifier #completionTest(7) -> empty
//@[49:50) NewLine |\n|
module
Expand Down
59 changes: 59 additions & 0 deletions src/Bicep.LangServer.UnitTests/Snippets/SnippetsProviderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,65 @@ public void GetResourceBodyCompletionSnippets_WithDiscriminatedObjectTypeAndRequ
});
}

[TestMethod]
public void GetModuleBodyCompletionSnippets_WithNoRequiredProperties_ShouldReturnEmptySnippet()
{
SnippetsProvider snippetsProvider = new SnippetsProvider();
var objectType = new ObjectType("objA", TypeSymbolValidationFlags.Default, new[]
{
new TypeProperty("name", LanguageConstants.String, TypePropertyFlags.ReadOnly),
new TypeProperty("location", LanguageConstants.String, TypePropertyFlags.WriteOnly),
new TypeProperty("id", LanguageConstants.String)
}, null);
TypeSymbol typeSymbol = new ModuleType("module", ResourceScope.Module, objectType);

IEnumerable<Snippet> snippets = snippetsProvider.GetModuleBodyCompletionSnippets(typeSymbol);

snippets.Should().SatisfyRespectively(
x =>
{
x.Prefix.Should().Be("{}");
x.Detail.Should().Be("{}");
x.CompletionPriority.Should().Be(CompletionPriority.Medium);
x.Text.Should().Be("{\n\t$0\n}");
});
}

[TestMethod]
public void GetModuleBodyCompletionSnippets_WithRequiredProperties_ShouldReturnEmptyAndRequiredPropertiesSnippets()
{
SnippetsProvider snippetsProvider = new SnippetsProvider();
var objectType = new ObjectType("objA", TypeSymbolValidationFlags.Default, new[]
{
new TypeProperty("name", LanguageConstants.String, TypePropertyFlags.Required),
new TypeProperty("location", LanguageConstants.String, TypePropertyFlags.Required),
new TypeProperty("id", LanguageConstants.String)
}, null);
TypeSymbol typeSymbol = new ModuleType("module", ResourceScope.Module, objectType);

IEnumerable<Snippet> snippets = snippetsProvider.GetModuleBodyCompletionSnippets(typeSymbol);

snippets.Should().SatisfyRespectively(
x =>
{
x.Prefix.Should().Be("{}");
x.Detail.Should().Be("{}");
x.CompletionPriority.Should().Be(CompletionPriority.Medium);
x.Text.Should().Be("{\n\t$0\n}");
},
x =>
{
x.Prefix.Should().Be("required-properties");
x.Detail.Should().Be("Required properties");
x.CompletionPriority.Should().Be(CompletionPriority.Medium);
x.Text.Should().BeEquivalentToIgnoringNewlines(@"{
name: $1
location: $2
$0
}");
});
}

private static ObjectType CreateObjectType(string name, params (string name, ITypeReference type, TypePropertyFlags typePropertyFlags)[] properties)
=> new(
name,
Expand Down
32 changes: 26 additions & 6 deletions src/Bicep.LangServer/Completions/BicepCompletionProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
using Bicep.Core.Semantics;
using Bicep.Core.Syntax;
using Bicep.Core.TypeSystem;
using Bicep.Core.TypeSystem.Az;
using Bicep.LanguageServer.Extensions;
using Bicep.LanguageServer.Snippets;
using OmniSharp.Extensions.LanguageServer.Protocol.Models;
Expand Down Expand Up @@ -57,7 +56,7 @@ public IEnumerable<CompletionItem> GetFilteredCompletions(Compilation compilatio
.Concat(GetResourceTypeCompletions(model, context))
.Concat(GetResourceTypeFollowerCompletions(context))
.Concat(GetModulePathCompletions(model, context))
.Concat(GetModuleBodyCompletions(context))
.Concat(GetModuleBodyCompletions(model, context))
.Concat(GetResourceBodyCompletions(model, context))
.Concat(GetParameterDefaultValueCompletions(model, context))
.Concat(GetVariableValueCompletions(context))
Expand Down Expand Up @@ -390,8 +389,7 @@ private IEnumerable<CompletionItem> CreateResourceBodyCompletions(SemanticModel
{
if (context.EnclosingDeclaration is ResourceDeclarationSyntax resourceDeclarationSyntax)
{
TypeSymbol typeSymbol = resourceDeclarationSyntax.GetDeclaredType(model.Binder, AzResourceTypeProvider.CreateWithAzTypes());

TypeSymbol typeSymbol = model.GetTypeInfo(resourceDeclarationSyntax);
IEnumerable<Snippet> snippets = SnippetsProvider.GetResourceBodyCompletionSnippets(typeSymbol, resourceDeclarationSyntax.IsExistingResource());

foreach (Snippet snippet in snippets)
Expand All @@ -406,11 +404,33 @@ private IEnumerable<CompletionItem> CreateResourceBodyCompletions(SemanticModel
}
}

private IEnumerable<CompletionItem> GetModuleBodyCompletions(BicepCompletionContext context)
private IEnumerable<CompletionItem> CreateModuleBodyCompletions(SemanticModel model, BicepCompletionContext context)
{
if (context.EnclosingDeclaration is ModuleDeclarationSyntax moduleDeclarationSyntax)
{
TypeSymbol typeSymbol = model.GetTypeInfo(moduleDeclarationSyntax);
IEnumerable<Snippet> snippets = SnippetsProvider.GetModuleBodyCompletionSnippets(typeSymbol);

foreach (Snippet snippet in snippets)
{
yield return CreateContextualSnippetCompletion(snippet!.Prefix,
snippet.Detail,
snippet.Text,
context.ReplacementRange,
snippet.CompletionPriority,
preselect: true);
}
}
}

private IEnumerable<CompletionItem> GetModuleBodyCompletions(SemanticModel model, BicepCompletionContext context)
{
if (context.Kind.HasFlag(BicepCompletionContextKind.ModuleBody))
{
yield return CreateObjectBodyCompletion(context.ReplacementRange);
foreach (CompletionItem completionItem in CreateModuleBodyCompletions(model, context))
{
yield return completionItem;
}

yield return CreateResourceOrModuleConditionCompletion(context.ReplacementRange);

Expand Down
2 changes: 2 additions & 0 deletions src/Bicep.LangServer/Snippets/ISnippetsProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ public interface ISnippetsProvider
{
IEnumerable<Snippet> GetTopLevelNamedDeclarationSnippets();

IEnumerable<Snippet> GetModuleBodyCompletionSnippets(TypeSymbol typeSymbol);

IEnumerable<Snippet> GetResourceBodyCompletionSnippets(TypeSymbol typeSymbol, bool isExistingResource);
}
}
Loading

0 comments on commit c8b397d

Please sign in to comment.