diff --git a/.gitattributes b/.gitattributes index 0142befda7c..c69f5440f63 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,3 +1,4 @@ *.bicep -text *.ts text eol=lf -*.cs text eol=lf \ No newline at end of file +*.cs text eol=lf +*.sh text eol=lf \ No newline at end of file diff --git a/Bicep.sln b/Bicep.sln index cb257db409e..42c884546e8 100644 --- a/Bicep.sln +++ b/Bicep.sln @@ -53,12 +53,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_SolutionItems", "_Solution NuGet.config = NuGet.config README.md = README.md SECURITY.md = SECURITY.md - SetBaseline.ps1 = SetBaseline.ps1 EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripts", "scripts", "{895557F9-3D8B-4752-878A-5F0AAF2E405C}" ProjectSection(SolutionItems) = preProject scripts\bcode.ps1 = scripts\bcode.ps1 + scripts\SetBaseline.ps1 = scripts\SetBaseline.ps1 EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{78549F2A-4BF8-4105-B4BB-EF8B2EF0ACCD}" diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cc0ec5f84f4..d3c18fd89af 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -35,7 +35,8 @@ Many of the bicep integration tests rely on baseline test assertion files that a #### Manually * If you see a test failure with a message containing Windows and *nix copy commands, you have encountered such a test. You have the following options to fix the test: 1. Manually execute the provided command in a shell. This makes sense for a single test, but is extremely tedious otherwise. - 1. Run the `SetBaseline.ps1` script at the repo root to execute the tests in `SetBaseLine` mode, which causes the baselines to be automatically updated in bulk for failing tests. You should see baseline file modifications in Git pending changes. (Make sure your Git pending changes are empty before doing so - your changes could get overwritten!). + 1. Run [`./scripts/SetBaseline.ps1`](./scripts/SetBaseline.ps1) (Windows) or [`./scripts/SetBaseline.sh`](./scripts/SetBaseline.sh) (Linux/OSX) to execute the tests in `SetBaseLine` mode. This automatically updates the baselines in bulk for failing tests. + 1. You should see baseline file modifications in Git pending changes. (Make sure your Git pending changes are empty before doing so - your changes could get overwritten!). * Inspect the baseline assertion diffs to ensure changes are expected and match the code changes you have made. (If a pull request contains changes to baseline files that can't be explained, it will not be merged.) #### Via GitHub Action @@ -55,7 +56,7 @@ If you have an active branch pushed to your GitHub fork, you can use the "Update * The name of the entry should match the name of the folder you create (same casing), and there should be a main.bicep file in that folder. 1. Make changes to main.bicep. 1. Create empty `main..bicep` assertion files in the folder. You need to create following suffixes: `diagnostics`, `formatted`, `symbols`, `syntax`, `tokens` - 1. Run `SetBaseline.ps1` to fill out the assertion files. + 1. Follow [Updating test baselines](#updating-test-baselines) to generate baseline files. * The naming and file structure is important here as it's used by the test runner to assert e.g. whether the example should compile, and the end-of-line characters. * For tests that deal with module, you may see some unexpected behavior because the test could be using a mock file resolver instead of the standard one. Similarly, some tests may be using a mock resource type provider instead of the standard ones - usually that explains why some types aren't recognized in tests. diff --git a/SetBaseline.ps1 b/scripts/SetBaseline.ps1 similarity index 100% rename from SetBaseline.ps1 rename to scripts/SetBaseline.ps1 diff --git a/scripts/SetBaseline.sh b/scripts/SetBaseline.sh new file mode 100755 index 00000000000..0e019c97e81 --- /dev/null +++ b/scripts/SetBaseline.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +dotnet test -- 'TestRunParameters.Parameter(name="SetBaseLine", value="true")' \ No newline at end of file diff --git a/src/Bicep.Core.IntegrationTests/DecoratorTests.cs b/src/Bicep.Core.IntegrationTests/DecoratorTests.cs index 9fbdb09e47c..b221632f4ff 100644 --- a/src/Bicep.Core.IntegrationTests/DecoratorTests.cs +++ b/src/Bicep.Core.IntegrationTests/DecoratorTests.cs @@ -104,7 +104,8 @@ public void NonDecoratorFunction_MissingDeclaration_CannotBeUsedAsDecorator() template.Should().NotHaveValue(); diagnostics.Should().HaveDiagnostics(new[] { ("BCP152", DiagnosticLevel.Error, "Function \"concat\" cannot be used as a decorator."), - ("BCP152", DiagnosticLevel.Error, "Function \"resourceId\" cannot be used as a decorator."), + ("BCP132", DiagnosticLevel.Error, "Expected a declaration after the decorator."), + ("BCP152", DiagnosticLevel.Error, "Function \"resourceId\" cannot be used as a decorator.") }); } } diff --git a/src/Bicep.Core.IntegrationTests/Emit/TemplateEmitterTests.cs b/src/Bicep.Core.IntegrationTests/Emit/TemplateEmitterTests.cs index 995922b3d5e..7536d88f0a3 100644 --- a/src/Bicep.Core.IntegrationTests/Emit/TemplateEmitterTests.cs +++ b/src/Bicep.Core.IntegrationTests/Emit/TemplateEmitterTests.cs @@ -37,8 +37,8 @@ public void ValidBicep_TemplateEmiterShouldProduceExpectedTemplate(DataSet dataS // emitting the template should be successful var result = this.EmitTemplate(SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), PathHelper.FilePathToFileUrl(bicepFilePath)), compiledFilePath, BicepTestConstants.DevAssemblyFileVersion); - result.Status.Should().Be(EmitStatus.Succeeded); result.Diagnostics.Should().BeEmptyOrContainDeprecatedDiagnosticOnly(); + result.Status.Should().Be(EmitStatus.Succeeded); var actual = JToken.Parse(File.ReadAllText(compiledFilePath)); @@ -57,8 +57,8 @@ public void TemplateEmitter_output_should_not_include_UTF8_BOM() // emitting the template should be successful var result = this.EmitTemplate(syntaxTreeGrouping, compiledFilePath, BicepTestConstants.DevAssemblyFileVersion); - result.Status.Should().Be(EmitStatus.Succeeded); result.Diagnostics.Should().BeEmpty(); + result.Status.Should().Be(EmitStatus.Succeeded); var bytes = File.ReadAllBytes(compiledFilePath); // No BOM at the start of the file @@ -102,8 +102,8 @@ public void InvalidBicep_TemplateEmiterShouldNotProduceAnyTemplate(DataSet dataS // emitting the template should fail var result = this.EmitTemplate(SyntaxTreeGroupingBuilder.Build(new FileResolver(), new Workspace(), PathHelper.FilePathToFileUrl(bicepFilePath)), filePath, BicepTestConstants.DevAssemblyFileVersion); - result.Status.Should().Be(EmitStatus.Failed); result.Diagnostics.Should().NotBeEmpty(); + result.Status.Should().Be(EmitStatus.Failed); } [DataTestMethod] diff --git a/src/Bicep.Core.IntegrationTests/ScenarioTests.cs b/src/Bicep.Core.IntegrationTests/ScenarioTests.cs index c82452b307d..c35c75b6a07 100644 --- a/src/Bicep.Core.IntegrationTests/ScenarioTests.cs +++ b/src/Bicep.Core.IntegrationTests/ScenarioTests.cs @@ -1524,5 +1524,24 @@ public void Test_Issue2009_expanded() evaluated.Should().HaveValueAtPath("$.outputs['providersApiVersionFirst'].value", "3024-01-01"); evaluated.Should().HaveValueAtPath("$.outputs['providersLocationFirst'].value", "Earth"); } + + [TestMethod] + public void Variable_loops_should_not_cause_infinite_recursion() + { + var result = CompilationHelper.Compile(@" +var loopInput = [ + 'one' + 'two' +] +var arrayOfObjectsViaLoop = [for (name, i) in loopInput: { + index: i + name: name + value: 'prefix-${i}-${name}-suffix' +}] +"); + + result.Should().NotHaveDiagnostics(); + result.Template.Should().NotBeNull(); + } } } \ No newline at end of file diff --git a/src/Bicep.Core.IntegrationTests/Semantics/SemanticModelTests.cs b/src/Bicep.Core.IntegrationTests/Semantics/SemanticModelTests.cs index ae9f6d76d9e..21697841666 100644 --- a/src/Bicep.Core.IntegrationTests/Semantics/SemanticModelTests.cs +++ b/src/Bicep.Core.IntegrationTests/Semantics/SemanticModelTests.cs @@ -17,6 +17,7 @@ using Bicep.Core.UnitTests.Assertions; using Bicep.Core.UnitTests.Utils; using FluentAssertions; +using FluentAssertions.Execution; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace Bicep.Core.IntegrationTests.Semantics @@ -158,7 +159,7 @@ public void FindReferencesResultsShouldIncludeAllSymbolReferenceSyntaxNodes(Data .SelectMany(s => semanticModel.FindReferences(s!)) .Where(refSyntax => !(refSyntax is INamedDeclarationSyntax)); - foundReferences.Should().BeEquivalentTo(symbolReferences); + symbolReferences.Should().BeSubsetOf(foundReferences); } private static List GetAllBoundSymbolReferences(ProgramSyntax program, SemanticModel semanticModel) @@ -168,7 +169,7 @@ private static List GetAllBoundSymbolReferences(ProgramSyntax progra new List(), (accumulated, current) => { - if (current is ISymbolReference symbolReference && TestSyntaxHelper.NodeShouldBeBound(symbolReference, semanticModel)) + if (current is ISymbolReference symbolReference && TestSyntaxHelper.NodeShouldBeBound(symbolReference)) { accumulated.Add(current); } diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/autoScalerPropertiesRequireEscaping.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/autoScalerPropertiesRequireEscaping.json index 6faf5cd6b5d..feed885e849 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/autoScalerPropertiesRequireEscaping.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/autoScalerPropertiesRequireEscaping.json @@ -5,7 +5,7 @@ "detail": "balance-similar-node-groups", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \n \n" }, "deprecated": false, "preselect": false, @@ -32,7 +32,7 @@ "detail": "expander", "documentation": { "kind": "markdown", - "value": "Type: `'least-waste' | 'most-pods' | 'random'` \n" + "value": "Type: `'least-waste' | 'most-pods' | 'random'` \n \n" }, "deprecated": false, "preselect": false, @@ -53,7 +53,7 @@ "detail": "max-empty-bulk-delete", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \n \n" }, "deprecated": false, "preselect": false, @@ -80,7 +80,7 @@ "detail": "max-graceful-termination-sec", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \n \n" }, "deprecated": false, "preselect": false, @@ -107,7 +107,7 @@ "detail": "max-total-unready-percentage", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \n \n" }, "deprecated": false, "preselect": false, @@ -134,7 +134,7 @@ "detail": "new-pod-scale-up-delay", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \n \n" }, "deprecated": false, "preselect": false, @@ -161,7 +161,7 @@ "detail": "ok-total-unready-count", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \n \n" }, "deprecated": false, "preselect": false, @@ -188,7 +188,7 @@ "detail": "scale-down-delay-after-add", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \n \n" }, "deprecated": false, "preselect": false, @@ -215,7 +215,7 @@ "detail": "scale-down-delay-after-delete", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \n \n" }, "deprecated": false, "preselect": false, @@ -242,7 +242,7 @@ "detail": "scale-down-delay-after-failure", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \n \n" }, "deprecated": false, "preselect": false, @@ -269,7 +269,7 @@ "detail": "scale-down-unneeded-time", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \n \n" }, "deprecated": false, "preselect": false, @@ -296,7 +296,7 @@ "detail": "scale-down-unready-time", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \n \n" }, "deprecated": false, "preselect": false, @@ -323,7 +323,7 @@ "detail": "scale-down-utilization-threshold", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \n \n" }, "deprecated": false, "preselect": false, @@ -350,7 +350,7 @@ "detail": "scan-interval", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \n \n" }, "deprecated": false, "preselect": false, @@ -377,7 +377,7 @@ "detail": "skip-nodes-with-local-storage", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \n \n" }, "deprecated": false, "preselect": false, @@ -404,7 +404,7 @@ "detail": "skip-nodes-with-system-pods", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \n \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccess.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccess.json index 49163123d47..053b2042832 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccess.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccess.json @@ -5,7 +5,7 @@ "detail": "arguments", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nCommand line arguments to pass to the script. Arguments are separated by spaces. ex: -Name blue* -Location 'West US 2' \n" }, "deprecated": false, "preselect": false, @@ -26,7 +26,7 @@ "detail": "azCliVersion (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nAzure CLI module version to be used. \n" }, "deprecated": false, "preselect": false, @@ -47,7 +47,7 @@ "detail": "cleanupPreference", "documentation": { "kind": "markdown", - "value": "Type: `'Always' | 'OnExpiration' | 'OnSuccess'` \n" + "value": "Type: `'Always' | 'OnExpiration' | 'OnSuccess'` \nThe clean up preference when the script execution gets in a terminal state. Default setting is 'Always'. \n" }, "deprecated": false, "preselect": false, @@ -68,7 +68,7 @@ "detail": "containerSettings", "documentation": { "kind": "markdown", - "value": "Type: `ContainerConfiguration` \n" + "value": "Type: `ContainerConfiguration` \nSettings to customize ACI container instance. \n" }, "deprecated": false, "preselect": false, @@ -89,7 +89,7 @@ "detail": "environmentVariables", "documentation": { "kind": "markdown", - "value": "Type: `EnvironmentVariable[]` \n" + "value": "Type: `EnvironmentVariable[]` \nThe environment variables to pass over to the script. \n" }, "deprecated": false, "preselect": false, @@ -110,7 +110,7 @@ "detail": "forceUpdateTag", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nGets or sets how the deployment script should be forced to execute even if the script resource has not changed. Can be current time stamp or a GUID. \n" }, "deprecated": false, "preselect": false, @@ -131,7 +131,7 @@ "detail": "outputs", "documentation": { "kind": "markdown", - "value": "Type: `Dictionary` \nRead-only property \n" + "value": "Type: `Dictionary` \nRead-only property \nList of script outputs. \n" }, "deprecated": false, "preselect": false, @@ -152,7 +152,7 @@ "detail": "primaryScriptUri", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nUri for the script. This is the entry point for the external script. \n" }, "deprecated": false, "preselect": false, @@ -173,7 +173,7 @@ "detail": "provisioningState", "documentation": { "kind": "markdown", - "value": "Type: `'Canceled' | 'Creating' | 'Failed' | 'ProvisioningResources' | 'Running' | 'Succeeded'` \nRead-only property \n" + "value": "Type: `'Canceled' | 'Creating' | 'Failed' | 'ProvisioningResources' | 'Running' | 'Succeeded'` \nRead-only property \nState of the script execution. This only appears in the response. \n" }, "deprecated": false, "preselect": false, @@ -194,7 +194,7 @@ "detail": "retentionInterval (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nInterval for which the service retains the script resource after it reaches a terminal state. Resource will be deleted when this duration expires. Duration is based on ISO 8601 pattern (for example P1D means one day). \n" }, "deprecated": false, "preselect": false, @@ -215,7 +215,7 @@ "detail": "scriptContent", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nScript body. \n" }, "deprecated": false, "preselect": false, @@ -236,7 +236,7 @@ "detail": "status", "documentation": { "kind": "markdown", - "value": "Type: `ScriptStatus` \nRead-only property \n" + "value": "Type: `ScriptStatus` \nRead-only property \nGeneric object modeling results of script execution. \n" }, "deprecated": false, "preselect": false, @@ -257,7 +257,7 @@ "detail": "storageAccountSettings", "documentation": { "kind": "markdown", - "value": "Type: `StorageAccountConfiguration` \n" + "value": "Type: `StorageAccountConfiguration` \nSettings to use an existing storage account. Valid storage account kinds are: Storage, StorageV2 and FileStorage \n" }, "deprecated": false, "preselect": false, @@ -278,7 +278,7 @@ "detail": "supportingScriptUris", "documentation": { "kind": "markdown", - "value": "Type: `string[]` \n" + "value": "Type: `string[]` \nSupporting files for the external script. \n" }, "deprecated": false, "preselect": false, @@ -299,7 +299,7 @@ "detail": "timeout", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nMaximum allowed script execution time specified in ISO 8601 format. Default value is P1D \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccessIndexesPlusSymbols.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccessIndexesPlusSymbols.json index 52b95c2c487..9c417f9ef46 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccessIndexesPlusSymbols.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccessIndexesPlusSymbols.json @@ -5,7 +5,7 @@ "detail": "arguments", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nCommand line arguments to pass to the script. Arguments are separated by spaces. ex: -Name blue* -Location 'West US 2' \n" }, "deprecated": false, "preselect": false, @@ -23,7 +23,7 @@ "detail": "azCliVersion (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nAzure CLI module version to be used. \n" }, "deprecated": false, "preselect": false, @@ -41,7 +41,7 @@ "detail": "cleanupPreference", "documentation": { "kind": "markdown", - "value": "Type: `'Always' | 'OnExpiration' | 'OnSuccess'` \n" + "value": "Type: `'Always' | 'OnExpiration' | 'OnSuccess'` \nThe clean up preference when the script execution gets in a terminal state. Default setting is 'Always'. \n" }, "deprecated": false, "preselect": false, @@ -59,7 +59,7 @@ "detail": "containerSettings", "documentation": { "kind": "markdown", - "value": "Type: `ContainerConfiguration` \n" + "value": "Type: `ContainerConfiguration` \nSettings to customize ACI container instance. \n" }, "deprecated": false, "preselect": false, @@ -77,7 +77,7 @@ "detail": "environmentVariables", "documentation": { "kind": "markdown", - "value": "Type: `EnvironmentVariable[]` \n" + "value": "Type: `EnvironmentVariable[]` \nThe environment variables to pass over to the script. \n" }, "deprecated": false, "preselect": false, @@ -95,7 +95,7 @@ "detail": "forceUpdateTag", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nGets or sets how the deployment script should be forced to execute even if the script resource has not changed. Can be current time stamp or a GUID. \n" }, "deprecated": false, "preselect": false, @@ -113,7 +113,7 @@ "detail": "outputs", "documentation": { "kind": "markdown", - "value": "Type: `Dictionary` \nRead-only property \n" + "value": "Type: `Dictionary` \nRead-only property \nList of script outputs. \n" }, "deprecated": false, "preselect": false, @@ -131,7 +131,7 @@ "detail": "primaryScriptUri", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nUri for the script. This is the entry point for the external script. \n" }, "deprecated": false, "preselect": false, @@ -149,7 +149,7 @@ "detail": "provisioningState", "documentation": { "kind": "markdown", - "value": "Type: `'Canceled' | 'Creating' | 'Failed' | 'ProvisioningResources' | 'Running' | 'Succeeded'` \nRead-only property \n" + "value": "Type: `'Canceled' | 'Creating' | 'Failed' | 'ProvisioningResources' | 'Running' | 'Succeeded'` \nRead-only property \nState of the script execution. This only appears in the response. \n" }, "deprecated": false, "preselect": false, @@ -167,7 +167,7 @@ "detail": "retentionInterval (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nInterval for which the service retains the script resource after it reaches a terminal state. Resource will be deleted when this duration expires. Duration is based on ISO 8601 pattern (for example P1D means one day). \n" }, "deprecated": false, "preselect": false, @@ -185,7 +185,7 @@ "detail": "scriptContent", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nScript body. \n" }, "deprecated": false, "preselect": false, @@ -203,7 +203,7 @@ "detail": "status", "documentation": { "kind": "markdown", - "value": "Type: `ScriptStatus` \nRead-only property \n" + "value": "Type: `ScriptStatus` \nRead-only property \nGeneric object modeling results of script execution. \n" }, "deprecated": false, "preselect": false, @@ -221,7 +221,7 @@ "detail": "storageAccountSettings", "documentation": { "kind": "markdown", - "value": "Type: `StorageAccountConfiguration` \n" + "value": "Type: `StorageAccountConfiguration` \nSettings to use an existing storage account. Valid storage account kinds are: Storage, StorageV2 and FileStorage \n" }, "deprecated": false, "preselect": false, @@ -239,7 +239,7 @@ "detail": "supportingScriptUris", "documentation": { "kind": "markdown", - "value": "Type: `string[]` \n" + "value": "Type: `string[]` \nSupporting files for the external script. \n" }, "deprecated": false, "preselect": false, @@ -257,7 +257,7 @@ "detail": "timeout", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nMaximum allowed script execution time specified in ISO 8601 format. Default value is P1D \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccessIndexesPlusSymbols_for.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccessIndexesPlusSymbols_for.json index 68bee94d093..ce806d0af6b 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccessIndexesPlusSymbols_for.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccessIndexesPlusSymbols_for.json @@ -5,7 +5,7 @@ "detail": "arguments", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nCommand line arguments to pass to the script. Arguments are separated by spaces. ex: -Name blue* -Location 'West US 2' \n" }, "deprecated": false, "preselect": false, @@ -23,7 +23,7 @@ "detail": "azCliVersion (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nAzure CLI module version to be used. \n" }, "deprecated": false, "preselect": false, @@ -41,7 +41,7 @@ "detail": "cleanupPreference", "documentation": { "kind": "markdown", - "value": "Type: `'Always' | 'OnExpiration' | 'OnSuccess'` \n" + "value": "Type: `'Always' | 'OnExpiration' | 'OnSuccess'` \nThe clean up preference when the script execution gets in a terminal state. Default setting is 'Always'. \n" }, "deprecated": false, "preselect": false, @@ -59,7 +59,7 @@ "detail": "containerSettings", "documentation": { "kind": "markdown", - "value": "Type: `ContainerConfiguration` \n" + "value": "Type: `ContainerConfiguration` \nSettings to customize ACI container instance. \n" }, "deprecated": false, "preselect": false, @@ -77,7 +77,7 @@ "detail": "environmentVariables", "documentation": { "kind": "markdown", - "value": "Type: `EnvironmentVariable[]` \n" + "value": "Type: `EnvironmentVariable[]` \nThe environment variables to pass over to the script. \n" }, "deprecated": false, "preselect": false, @@ -95,7 +95,7 @@ "detail": "forceUpdateTag", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nGets or sets how the deployment script should be forced to execute even if the script resource has not changed. Can be current time stamp or a GUID. \n" }, "deprecated": false, "preselect": false, @@ -113,7 +113,7 @@ "detail": "outputs", "documentation": { "kind": "markdown", - "value": "Type: `Dictionary` \nRead-only property \n" + "value": "Type: `Dictionary` \nRead-only property \nList of script outputs. \n" }, "deprecated": false, "preselect": false, @@ -131,7 +131,7 @@ "detail": "primaryScriptUri", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nUri for the script. This is the entry point for the external script. \n" }, "deprecated": false, "preselect": false, @@ -149,7 +149,7 @@ "detail": "provisioningState", "documentation": { "kind": "markdown", - "value": "Type: `'Canceled' | 'Creating' | 'Failed' | 'ProvisioningResources' | 'Running' | 'Succeeded'` \nRead-only property \n" + "value": "Type: `'Canceled' | 'Creating' | 'Failed' | 'ProvisioningResources' | 'Running' | 'Succeeded'` \nRead-only property \nState of the script execution. This only appears in the response. \n" }, "deprecated": false, "preselect": false, @@ -167,7 +167,7 @@ "detail": "retentionInterval (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nInterval for which the service retains the script resource after it reaches a terminal state. Resource will be deleted when this duration expires. Duration is based on ISO 8601 pattern (for example P1D means one day). \n" }, "deprecated": false, "preselect": false, @@ -185,7 +185,7 @@ "detail": "scriptContent", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nScript body. \n" }, "deprecated": false, "preselect": false, @@ -203,7 +203,7 @@ "detail": "status", "documentation": { "kind": "markdown", - "value": "Type: `ScriptStatus` \nRead-only property \n" + "value": "Type: `ScriptStatus` \nRead-only property \nGeneric object modeling results of script execution. \n" }, "deprecated": false, "preselect": false, @@ -221,7 +221,7 @@ "detail": "storageAccountSettings", "documentation": { "kind": "markdown", - "value": "Type: `StorageAccountConfiguration` \n" + "value": "Type: `StorageAccountConfiguration` \nSettings to use an existing storage account. Valid storage account kinds are: Storage, StorageV2 and FileStorage \n" }, "deprecated": false, "preselect": false, @@ -239,7 +239,7 @@ "detail": "supportingScriptUris", "documentation": { "kind": "markdown", - "value": "Type: `string[]` \n" + "value": "Type: `string[]` \nSupporting files for the external script. \n" }, "deprecated": false, "preselect": false, @@ -257,7 +257,7 @@ "detail": "timeout", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nMaximum allowed script execution time specified in ISO 8601 format. Default value is P1D \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccessIndexesPlusSymbols_for_if.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccessIndexesPlusSymbols_for_if.json index c0707ad008a..a45d09021e6 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccessIndexesPlusSymbols_for_if.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccessIndexesPlusSymbols_for_if.json @@ -5,7 +5,7 @@ "detail": "arguments", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nCommand line arguments to pass to the script. Arguments are separated by spaces. ex: -Name blue* -Location 'West US 2' \n" }, "deprecated": false, "preselect": false, @@ -23,7 +23,7 @@ "detail": "azCliVersion (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nAzure CLI module version to be used. \n" }, "deprecated": false, "preselect": false, @@ -41,7 +41,7 @@ "detail": "cleanupPreference", "documentation": { "kind": "markdown", - "value": "Type: `'Always' | 'OnExpiration' | 'OnSuccess'` \n" + "value": "Type: `'Always' | 'OnExpiration' | 'OnSuccess'` \nThe clean up preference when the script execution gets in a terminal state. Default setting is 'Always'. \n" }, "deprecated": false, "preselect": false, @@ -59,7 +59,7 @@ "detail": "containerSettings", "documentation": { "kind": "markdown", - "value": "Type: `ContainerConfiguration` \n" + "value": "Type: `ContainerConfiguration` \nSettings to customize ACI container instance. \n" }, "deprecated": false, "preselect": false, @@ -77,7 +77,7 @@ "detail": "environmentVariables", "documentation": { "kind": "markdown", - "value": "Type: `EnvironmentVariable[]` \n" + "value": "Type: `EnvironmentVariable[]` \nThe environment variables to pass over to the script. \n" }, "deprecated": false, "preselect": false, @@ -95,7 +95,7 @@ "detail": "forceUpdateTag", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nGets or sets how the deployment script should be forced to execute even if the script resource has not changed. Can be current time stamp or a GUID. \n" }, "deprecated": false, "preselect": false, @@ -113,7 +113,7 @@ "detail": "outputs", "documentation": { "kind": "markdown", - "value": "Type: `Dictionary` \nRead-only property \n" + "value": "Type: `Dictionary` \nRead-only property \nList of script outputs. \n" }, "deprecated": false, "preselect": false, @@ -131,7 +131,7 @@ "detail": "primaryScriptUri", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nUri for the script. This is the entry point for the external script. \n" }, "deprecated": false, "preselect": false, @@ -149,7 +149,7 @@ "detail": "provisioningState", "documentation": { "kind": "markdown", - "value": "Type: `'Canceled' | 'Creating' | 'Failed' | 'ProvisioningResources' | 'Running' | 'Succeeded'` \nRead-only property \n" + "value": "Type: `'Canceled' | 'Creating' | 'Failed' | 'ProvisioningResources' | 'Running' | 'Succeeded'` \nRead-only property \nState of the script execution. This only appears in the response. \n" }, "deprecated": false, "preselect": false, @@ -167,7 +167,7 @@ "detail": "retentionInterval (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nInterval for which the service retains the script resource after it reaches a terminal state. Resource will be deleted when this duration expires. Duration is based on ISO 8601 pattern (for example P1D means one day). \n" }, "deprecated": false, "preselect": false, @@ -185,7 +185,7 @@ "detail": "scriptContent", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nScript body. \n" }, "deprecated": false, "preselect": false, @@ -203,7 +203,7 @@ "detail": "status", "documentation": { "kind": "markdown", - "value": "Type: `ScriptStatus` \nRead-only property \n" + "value": "Type: `ScriptStatus` \nRead-only property \nGeneric object modeling results of script execution. \n" }, "deprecated": false, "preselect": false, @@ -221,7 +221,7 @@ "detail": "storageAccountSettings", "documentation": { "kind": "markdown", - "value": "Type: `StorageAccountConfiguration` \n" + "value": "Type: `StorageAccountConfiguration` \nSettings to use an existing storage account. Valid storage account kinds are: Storage, StorageV2 and FileStorage \n" }, "deprecated": false, "preselect": false, @@ -239,7 +239,7 @@ "detail": "supportingScriptUris", "documentation": { "kind": "markdown", - "value": "Type: `string[]` \n" + "value": "Type: `string[]` \nSupporting files for the external script. \n" }, "deprecated": false, "preselect": false, @@ -257,7 +257,7 @@ "detail": "timeout", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nMaximum allowed script execution time specified in ISO 8601 format. Default value is P1D \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccessIndexesPlusSymbols_if.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccessIndexesPlusSymbols_if.json index 6e871aa338d..420bdf3287f 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccessIndexesPlusSymbols_if.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/cliPropertyAccessIndexesPlusSymbols_if.json @@ -5,7 +5,7 @@ "detail": "arguments", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nCommand line arguments to pass to the script. Arguments are separated by spaces. ex: -Name blue* -Location 'West US 2' \n" }, "deprecated": false, "preselect": false, @@ -23,7 +23,7 @@ "detail": "azCliVersion (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nAzure CLI module version to be used. \n" }, "deprecated": false, "preselect": false, @@ -41,7 +41,7 @@ "detail": "cleanupPreference", "documentation": { "kind": "markdown", - "value": "Type: `'Always' | 'OnExpiration' | 'OnSuccess'` \n" + "value": "Type: `'Always' | 'OnExpiration' | 'OnSuccess'` \nThe clean up preference when the script execution gets in a terminal state. Default setting is 'Always'. \n" }, "deprecated": false, "preselect": false, @@ -59,7 +59,7 @@ "detail": "containerSettings", "documentation": { "kind": "markdown", - "value": "Type: `ContainerConfiguration` \n" + "value": "Type: `ContainerConfiguration` \nSettings to customize ACI container instance. \n" }, "deprecated": false, "preselect": false, @@ -77,7 +77,7 @@ "detail": "environmentVariables", "documentation": { "kind": "markdown", - "value": "Type: `EnvironmentVariable[]` \n" + "value": "Type: `EnvironmentVariable[]` \nThe environment variables to pass over to the script. \n" }, "deprecated": false, "preselect": false, @@ -95,7 +95,7 @@ "detail": "forceUpdateTag", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nGets or sets how the deployment script should be forced to execute even if the script resource has not changed. Can be current time stamp or a GUID. \n" }, "deprecated": false, "preselect": false, @@ -113,7 +113,7 @@ "detail": "outputs", "documentation": { "kind": "markdown", - "value": "Type: `Dictionary` \nRead-only property \n" + "value": "Type: `Dictionary` \nRead-only property \nList of script outputs. \n" }, "deprecated": false, "preselect": false, @@ -131,7 +131,7 @@ "detail": "primaryScriptUri", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nUri for the script. This is the entry point for the external script. \n" }, "deprecated": false, "preselect": false, @@ -149,7 +149,7 @@ "detail": "provisioningState", "documentation": { "kind": "markdown", - "value": "Type: `'Canceled' | 'Creating' | 'Failed' | 'ProvisioningResources' | 'Running' | 'Succeeded'` \nRead-only property \n" + "value": "Type: `'Canceled' | 'Creating' | 'Failed' | 'ProvisioningResources' | 'Running' | 'Succeeded'` \nRead-only property \nState of the script execution. This only appears in the response. \n" }, "deprecated": false, "preselect": false, @@ -167,7 +167,7 @@ "detail": "retentionInterval (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nInterval for which the service retains the script resource after it reaches a terminal state. Resource will be deleted when this duration expires. Duration is based on ISO 8601 pattern (for example P1D means one day). \n" }, "deprecated": false, "preselect": false, @@ -185,7 +185,7 @@ "detail": "scriptContent", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nScript body. \n" }, "deprecated": false, "preselect": false, @@ -203,7 +203,7 @@ "detail": "status", "documentation": { "kind": "markdown", - "value": "Type: `ScriptStatus` \nRead-only property \n" + "value": "Type: `ScriptStatus` \nRead-only property \nGeneric object modeling results of script execution. \n" }, "deprecated": false, "preselect": false, @@ -221,7 +221,7 @@ "detail": "storageAccountSettings", "documentation": { "kind": "markdown", - "value": "Type: `StorageAccountConfiguration` \n" + "value": "Type: `StorageAccountConfiguration` \nSettings to use an existing storage account. Valid storage account kinds are: Storage, StorageV2 and FileStorage \n" }, "deprecated": false, "preselect": false, @@ -239,7 +239,7 @@ "detail": "supportingScriptUris", "documentation": { "kind": "markdown", - "value": "Type: `string[]` \n" + "value": "Type: `string[]` \nSupporting files for the external script. \n" }, "deprecated": false, "preselect": false, @@ -257,7 +257,7 @@ "detail": "timeout", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nMaximum allowed script execution time specified in ISO 8601 format. Default value is P1D \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeIndexes.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeIndexes.json index 9081da2b4d6..e4513587005 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeIndexes.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeIndexes.json @@ -5,7 +5,7 @@ "detail": "apiProperties", "documentation": { "kind": "markdown", - "value": "Type: `ApiProperties` \n" + "value": "Type: `ApiProperties` \n \n" }, "deprecated": false, "preselect": false, @@ -23,7 +23,7 @@ "detail": "backupPolicy", "documentation": { "kind": "markdown", - "value": "Type: `BackupPolicy` \n" + "value": "Type: `BackupPolicy` \nThe object representing the policy for taking backups on an account. \n" }, "deprecated": false, "preselect": false, @@ -41,7 +41,7 @@ "detail": "capabilities", "documentation": { "kind": "markdown", - "value": "Type: `Capability[]` \n" + "value": "Type: `Capability[]` \nList of Cosmos DB capabilities for the account \n" }, "deprecated": false, "preselect": false, @@ -59,7 +59,7 @@ "detail": "connectorOffer", "documentation": { "kind": "markdown", - "value": "Type: `'Small'` \n" + "value": "Type: `'Small'` \nThe cassandra connector offer type for the Cosmos DB database C* account. \n" }, "deprecated": false, "preselect": false, @@ -77,7 +77,7 @@ "detail": "consistencyPolicy", "documentation": { "kind": "markdown", - "value": "Type: `ConsistencyPolicy` \n" + "value": "Type: `ConsistencyPolicy` \nThe consistency policy for the Cosmos DB database account. \n" }, "deprecated": false, "preselect": false, @@ -95,7 +95,7 @@ "detail": "cors", "documentation": { "kind": "markdown", - "value": "Type: `CorsPolicy[]` \n" + "value": "Type: `CorsPolicy[]` \nThe CORS policy for the Cosmos DB database account. \n" }, "deprecated": false, "preselect": false, @@ -113,7 +113,7 @@ "detail": "createMode (Required)", "documentation": { "kind": "markdown", - "value": "Type: `'Default'` \n" + "value": "Type: `'Default'` \nProperties for non-restore Azure Cosmos DB database account requests. \n" }, "deprecated": false, "preselect": false, @@ -131,7 +131,7 @@ "detail": "databaseAccountOfferType (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe offer type for the database \n" }, "deprecated": false, "preselect": false, @@ -149,7 +149,7 @@ "detail": "disableKeyBasedMetadataWriteAccess", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nDisable write operations on metadata resources (databases, containers, throughput) via account keys \n" }, "deprecated": false, "preselect": false, @@ -167,7 +167,7 @@ "detail": "documentEndpoint", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nThe connection endpoint for the Cosmos DB database account. \n" }, "deprecated": false, "preselect": false, @@ -185,7 +185,7 @@ "detail": "enableAnalyticalStorage", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nFlag to indicate whether to enable storage analytics. \n" }, "deprecated": false, "preselect": false, @@ -203,7 +203,7 @@ "detail": "enableAutomaticFailover", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nEnables automatic failover of the write region in the rare event that the region is unavailable due to an outage. Automatic failover will result in a new write region for the account and is chosen based on the failover priorities configured for the account. \n" }, "deprecated": false, "preselect": false, @@ -221,7 +221,7 @@ "detail": "enableCassandraConnector", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nEnables the cassandra connector on the Cosmos DB C* account \n" }, "deprecated": false, "preselect": false, @@ -239,7 +239,7 @@ "detail": "enableFreeTier", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nFlag to indicate whether Free Tier is enabled. \n" }, "deprecated": false, "preselect": false, @@ -257,7 +257,7 @@ "detail": "enableMultipleWriteLocations", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nEnables the account to write in multiple locations \n" }, "deprecated": false, "preselect": false, @@ -275,7 +275,7 @@ "detail": "failoverPolicies", "documentation": { "kind": "markdown", - "value": "Type: `FailoverPolicy[]` \nRead-only property \n" + "value": "Type: `FailoverPolicy[]` \nRead-only property \nAn array that contains the regions ordered by their failover priorities. \n" }, "deprecated": false, "preselect": false, @@ -293,7 +293,7 @@ "detail": "instanceId", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nA unique identifier assigned to the database account \n" }, "deprecated": false, "preselect": false, @@ -311,7 +311,7 @@ "detail": "ipRules", "documentation": { "kind": "markdown", - "value": "Type: `IpAddressOrRange[]` \n" + "value": "Type: `IpAddressOrRange[]` \nArray of IpAddressOrRange objects. \n" }, "deprecated": false, "preselect": false, @@ -329,7 +329,7 @@ "detail": "isVirtualNetworkFilterEnabled", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nFlag to indicate whether to enable/disable Virtual Network ACL rules. \n" }, "deprecated": false, "preselect": false, @@ -347,7 +347,7 @@ "detail": "keyVaultKeyUri", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe URI of the key vault \n" }, "deprecated": false, "preselect": false, @@ -365,7 +365,7 @@ "detail": "locations (Required)", "documentation": { "kind": "markdown", - "value": "Type: `Location[]` \n" + "value": "Type: `Location[]` \nAn array that contains the georeplication locations enabled for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -383,7 +383,7 @@ "detail": "privateEndpointConnections", "documentation": { "kind": "markdown", - "value": "Type: `PrivateEndpointConnection[]` \nRead-only property \n" + "value": "Type: `PrivateEndpointConnection[]` \nRead-only property \nList of Private Endpoint Connections configured for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -401,7 +401,7 @@ "detail": "provisioningState", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nThe status of the Cosmos DB account at the time the operation was called. The status can be one of following. 'Creating' – the Cosmos DB account is being created. When an account is in Creating state, only properties that are specified as input for the Create Cosmos DB account operation are returned. 'Succeeded' – the Cosmos DB account is active for use. 'Updating' – the Cosmos DB account is being updated. 'Deleting' – the Cosmos DB account is being deleted. 'Failed' – the Cosmos DB account failed creation. 'DeletionFailed' – the Cosmos DB account deletion failed. \n" }, "deprecated": false, "preselect": false, @@ -419,7 +419,7 @@ "detail": "publicNetworkAccess", "documentation": { "kind": "markdown", - "value": "Type: `'Disabled' | 'Enabled'` \n" + "value": "Type: `'Disabled' | 'Enabled'` \nWhether requests from Public Network are allowed. \n" }, "deprecated": false, "preselect": false, @@ -437,7 +437,7 @@ "detail": "readLocations", "documentation": { "kind": "markdown", - "value": "Type: `Location[]` \nRead-only property \n" + "value": "Type: `Location[]` \nRead-only property \nAn array that contains of the read locations enabled for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -455,7 +455,7 @@ "detail": "restoreParameters", "documentation": { "kind": "markdown", - "value": "Type: `RestoreParameters` \nRead-only property \n" + "value": "Type: `RestoreParameters` \nRead-only property \nParameters to indicate the information about the restore. \n" }, "deprecated": false, "preselect": false, @@ -473,7 +473,7 @@ "detail": "virtualNetworkRules", "documentation": { "kind": "markdown", - "value": "Type: `VirtualNetworkRule[]` \n" + "value": "Type: `VirtualNetworkRule[]` \nList of Virtual Network ACL rules configured for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -491,7 +491,7 @@ "detail": "writeLocations", "documentation": { "kind": "markdown", - "value": "Type: `Location[]` \nRead-only property \n" + "value": "Type: `Location[]` \nRead-only property \nAn array that contains the write location for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeIndexes_for.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeIndexes_for.json index 0bca6a76c83..41fa819edc3 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeIndexes_for.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeIndexes_for.json @@ -5,7 +5,7 @@ "detail": "apiProperties", "documentation": { "kind": "markdown", - "value": "Type: `ApiProperties` \n" + "value": "Type: `ApiProperties` \n \n" }, "deprecated": false, "preselect": false, @@ -23,7 +23,7 @@ "detail": "backupPolicy", "documentation": { "kind": "markdown", - "value": "Type: `BackupPolicy` \n" + "value": "Type: `BackupPolicy` \nThe object representing the policy for taking backups on an account. \n" }, "deprecated": false, "preselect": false, @@ -41,7 +41,7 @@ "detail": "capabilities", "documentation": { "kind": "markdown", - "value": "Type: `Capability[]` \n" + "value": "Type: `Capability[]` \nList of Cosmos DB capabilities for the account \n" }, "deprecated": false, "preselect": false, @@ -59,7 +59,7 @@ "detail": "connectorOffer", "documentation": { "kind": "markdown", - "value": "Type: `'Small'` \n" + "value": "Type: `'Small'` \nThe cassandra connector offer type for the Cosmos DB database C* account. \n" }, "deprecated": false, "preselect": false, @@ -77,7 +77,7 @@ "detail": "consistencyPolicy", "documentation": { "kind": "markdown", - "value": "Type: `ConsistencyPolicy` \n" + "value": "Type: `ConsistencyPolicy` \nThe consistency policy for the Cosmos DB database account. \n" }, "deprecated": false, "preselect": false, @@ -95,7 +95,7 @@ "detail": "cors", "documentation": { "kind": "markdown", - "value": "Type: `CorsPolicy[]` \n" + "value": "Type: `CorsPolicy[]` \nThe CORS policy for the Cosmos DB database account. \n" }, "deprecated": false, "preselect": false, @@ -113,7 +113,7 @@ "detail": "createMode (Required)", "documentation": { "kind": "markdown", - "value": "Type: `'Default'` \n" + "value": "Type: `'Default'` \nProperties for non-restore Azure Cosmos DB database account requests. \n" }, "deprecated": false, "preselect": false, @@ -131,7 +131,7 @@ "detail": "databaseAccountOfferType (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe offer type for the database \n" }, "deprecated": false, "preselect": false, @@ -149,7 +149,7 @@ "detail": "disableKeyBasedMetadataWriteAccess", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nDisable write operations on metadata resources (databases, containers, throughput) via account keys \n" }, "deprecated": false, "preselect": false, @@ -167,7 +167,7 @@ "detail": "documentEndpoint", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nThe connection endpoint for the Cosmos DB database account. \n" }, "deprecated": false, "preselect": false, @@ -185,7 +185,7 @@ "detail": "enableAnalyticalStorage", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nFlag to indicate whether to enable storage analytics. \n" }, "deprecated": false, "preselect": false, @@ -203,7 +203,7 @@ "detail": "enableAutomaticFailover", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nEnables automatic failover of the write region in the rare event that the region is unavailable due to an outage. Automatic failover will result in a new write region for the account and is chosen based on the failover priorities configured for the account. \n" }, "deprecated": false, "preselect": false, @@ -221,7 +221,7 @@ "detail": "enableCassandraConnector", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nEnables the cassandra connector on the Cosmos DB C* account \n" }, "deprecated": false, "preselect": false, @@ -239,7 +239,7 @@ "detail": "enableFreeTier", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nFlag to indicate whether Free Tier is enabled. \n" }, "deprecated": false, "preselect": false, @@ -257,7 +257,7 @@ "detail": "enableMultipleWriteLocations", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nEnables the account to write in multiple locations \n" }, "deprecated": false, "preselect": false, @@ -275,7 +275,7 @@ "detail": "failoverPolicies", "documentation": { "kind": "markdown", - "value": "Type: `FailoverPolicy[]` \nRead-only property \n" + "value": "Type: `FailoverPolicy[]` \nRead-only property \nAn array that contains the regions ordered by their failover priorities. \n" }, "deprecated": false, "preselect": false, @@ -293,7 +293,7 @@ "detail": "instanceId", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nA unique identifier assigned to the database account \n" }, "deprecated": false, "preselect": false, @@ -311,7 +311,7 @@ "detail": "ipRules", "documentation": { "kind": "markdown", - "value": "Type: `IpAddressOrRange[]` \n" + "value": "Type: `IpAddressOrRange[]` \nArray of IpAddressOrRange objects. \n" }, "deprecated": false, "preselect": false, @@ -329,7 +329,7 @@ "detail": "isVirtualNetworkFilterEnabled", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nFlag to indicate whether to enable/disable Virtual Network ACL rules. \n" }, "deprecated": false, "preselect": false, @@ -347,7 +347,7 @@ "detail": "keyVaultKeyUri", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe URI of the key vault \n" }, "deprecated": false, "preselect": false, @@ -365,7 +365,7 @@ "detail": "locations (Required)", "documentation": { "kind": "markdown", - "value": "Type: `Location[]` \n" + "value": "Type: `Location[]` \nAn array that contains the georeplication locations enabled for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -383,7 +383,7 @@ "detail": "privateEndpointConnections", "documentation": { "kind": "markdown", - "value": "Type: `PrivateEndpointConnection[]` \nRead-only property \n" + "value": "Type: `PrivateEndpointConnection[]` \nRead-only property \nList of Private Endpoint Connections configured for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -401,7 +401,7 @@ "detail": "provisioningState", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nThe status of the Cosmos DB account at the time the operation was called. The status can be one of following. 'Creating' – the Cosmos DB account is being created. When an account is in Creating state, only properties that are specified as input for the Create Cosmos DB account operation are returned. 'Succeeded' – the Cosmos DB account is active for use. 'Updating' – the Cosmos DB account is being updated. 'Deleting' – the Cosmos DB account is being deleted. 'Failed' – the Cosmos DB account failed creation. 'DeletionFailed' – the Cosmos DB account deletion failed. \n" }, "deprecated": false, "preselect": false, @@ -419,7 +419,7 @@ "detail": "publicNetworkAccess", "documentation": { "kind": "markdown", - "value": "Type: `'Disabled' | 'Enabled'` \n" + "value": "Type: `'Disabled' | 'Enabled'` \nWhether requests from Public Network are allowed. \n" }, "deprecated": false, "preselect": false, @@ -437,7 +437,7 @@ "detail": "readLocations", "documentation": { "kind": "markdown", - "value": "Type: `Location[]` \nRead-only property \n" + "value": "Type: `Location[]` \nRead-only property \nAn array that contains of the read locations enabled for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -455,7 +455,7 @@ "detail": "restoreParameters", "documentation": { "kind": "markdown", - "value": "Type: `RestoreParameters` \nRead-only property \n" + "value": "Type: `RestoreParameters` \nRead-only property \nParameters to indicate the information about the restore. \n" }, "deprecated": false, "preselect": false, @@ -473,7 +473,7 @@ "detail": "virtualNetworkRules", "documentation": { "kind": "markdown", - "value": "Type: `VirtualNetworkRule[]` \n" + "value": "Type: `VirtualNetworkRule[]` \nList of Virtual Network ACL rules configured for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -491,7 +491,7 @@ "detail": "writeLocations", "documentation": { "kind": "markdown", - "value": "Type: `Location[]` \nRead-only property \n" + "value": "Type: `Location[]` \nRead-only property \nAn array that contains the write location for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeIndexes_for_if.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeIndexes_for_if.json index 8ee762cb74f..c1d3b2dc466 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeIndexes_for_if.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeIndexes_for_if.json @@ -5,7 +5,7 @@ "detail": "apiProperties", "documentation": { "kind": "markdown", - "value": "Type: `ApiProperties` \n" + "value": "Type: `ApiProperties` \n \n" }, "deprecated": false, "preselect": false, @@ -23,7 +23,7 @@ "detail": "backupPolicy", "documentation": { "kind": "markdown", - "value": "Type: `BackupPolicy` \n" + "value": "Type: `BackupPolicy` \nThe object representing the policy for taking backups on an account. \n" }, "deprecated": false, "preselect": false, @@ -41,7 +41,7 @@ "detail": "capabilities", "documentation": { "kind": "markdown", - "value": "Type: `Capability[]` \n" + "value": "Type: `Capability[]` \nList of Cosmos DB capabilities for the account \n" }, "deprecated": false, "preselect": false, @@ -59,7 +59,7 @@ "detail": "connectorOffer", "documentation": { "kind": "markdown", - "value": "Type: `'Small'` \n" + "value": "Type: `'Small'` \nThe cassandra connector offer type for the Cosmos DB database C* account. \n" }, "deprecated": false, "preselect": false, @@ -77,7 +77,7 @@ "detail": "consistencyPolicy", "documentation": { "kind": "markdown", - "value": "Type: `ConsistencyPolicy` \n" + "value": "Type: `ConsistencyPolicy` \nThe consistency policy for the Cosmos DB database account. \n" }, "deprecated": false, "preselect": false, @@ -95,7 +95,7 @@ "detail": "cors", "documentation": { "kind": "markdown", - "value": "Type: `CorsPolicy[]` \n" + "value": "Type: `CorsPolicy[]` \nThe CORS policy for the Cosmos DB database account. \n" }, "deprecated": false, "preselect": false, @@ -113,7 +113,7 @@ "detail": "createMode (Required)", "documentation": { "kind": "markdown", - "value": "Type: `'Default'` \n" + "value": "Type: `'Default'` \nProperties for non-restore Azure Cosmos DB database account requests. \n" }, "deprecated": false, "preselect": false, @@ -131,7 +131,7 @@ "detail": "databaseAccountOfferType (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe offer type for the database \n" }, "deprecated": false, "preselect": false, @@ -149,7 +149,7 @@ "detail": "disableKeyBasedMetadataWriteAccess", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nDisable write operations on metadata resources (databases, containers, throughput) via account keys \n" }, "deprecated": false, "preselect": false, @@ -167,7 +167,7 @@ "detail": "documentEndpoint", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nThe connection endpoint for the Cosmos DB database account. \n" }, "deprecated": false, "preselect": false, @@ -185,7 +185,7 @@ "detail": "enableAnalyticalStorage", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nFlag to indicate whether to enable storage analytics. \n" }, "deprecated": false, "preselect": false, @@ -203,7 +203,7 @@ "detail": "enableAutomaticFailover", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nEnables automatic failover of the write region in the rare event that the region is unavailable due to an outage. Automatic failover will result in a new write region for the account and is chosen based on the failover priorities configured for the account. \n" }, "deprecated": false, "preselect": false, @@ -221,7 +221,7 @@ "detail": "enableCassandraConnector", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nEnables the cassandra connector on the Cosmos DB C* account \n" }, "deprecated": false, "preselect": false, @@ -239,7 +239,7 @@ "detail": "enableFreeTier", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nFlag to indicate whether Free Tier is enabled. \n" }, "deprecated": false, "preselect": false, @@ -257,7 +257,7 @@ "detail": "enableMultipleWriteLocations", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nEnables the account to write in multiple locations \n" }, "deprecated": false, "preselect": false, @@ -275,7 +275,7 @@ "detail": "failoverPolicies", "documentation": { "kind": "markdown", - "value": "Type: `FailoverPolicy[]` \nRead-only property \n" + "value": "Type: `FailoverPolicy[]` \nRead-only property \nAn array that contains the regions ordered by their failover priorities. \n" }, "deprecated": false, "preselect": false, @@ -293,7 +293,7 @@ "detail": "instanceId", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nA unique identifier assigned to the database account \n" }, "deprecated": false, "preselect": false, @@ -311,7 +311,7 @@ "detail": "ipRules", "documentation": { "kind": "markdown", - "value": "Type: `IpAddressOrRange[]` \n" + "value": "Type: `IpAddressOrRange[]` \nArray of IpAddressOrRange objects. \n" }, "deprecated": false, "preselect": false, @@ -329,7 +329,7 @@ "detail": "isVirtualNetworkFilterEnabled", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nFlag to indicate whether to enable/disable Virtual Network ACL rules. \n" }, "deprecated": false, "preselect": false, @@ -347,7 +347,7 @@ "detail": "keyVaultKeyUri", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe URI of the key vault \n" }, "deprecated": false, "preselect": false, @@ -365,7 +365,7 @@ "detail": "locations (Required)", "documentation": { "kind": "markdown", - "value": "Type: `Location[]` \n" + "value": "Type: `Location[]` \nAn array that contains the georeplication locations enabled for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -383,7 +383,7 @@ "detail": "privateEndpointConnections", "documentation": { "kind": "markdown", - "value": "Type: `PrivateEndpointConnection[]` \nRead-only property \n" + "value": "Type: `PrivateEndpointConnection[]` \nRead-only property \nList of Private Endpoint Connections configured for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -401,7 +401,7 @@ "detail": "provisioningState", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nThe status of the Cosmos DB account at the time the operation was called. The status can be one of following. 'Creating' – the Cosmos DB account is being created. When an account is in Creating state, only properties that are specified as input for the Create Cosmos DB account operation are returned. 'Succeeded' – the Cosmos DB account is active for use. 'Updating' – the Cosmos DB account is being updated. 'Deleting' – the Cosmos DB account is being deleted. 'Failed' – the Cosmos DB account failed creation. 'DeletionFailed' – the Cosmos DB account deletion failed. \n" }, "deprecated": false, "preselect": false, @@ -419,7 +419,7 @@ "detail": "publicNetworkAccess", "documentation": { "kind": "markdown", - "value": "Type: `'Disabled' | 'Enabled'` \n" + "value": "Type: `'Disabled' | 'Enabled'` \nWhether requests from Public Network are allowed. \n" }, "deprecated": false, "preselect": false, @@ -437,7 +437,7 @@ "detail": "readLocations", "documentation": { "kind": "markdown", - "value": "Type: `Location[]` \nRead-only property \n" + "value": "Type: `Location[]` \nRead-only property \nAn array that contains of the read locations enabled for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -455,7 +455,7 @@ "detail": "restoreParameters", "documentation": { "kind": "markdown", - "value": "Type: `RestoreParameters` \nRead-only property \n" + "value": "Type: `RestoreParameters` \nRead-only property \nParameters to indicate the information about the restore. \n" }, "deprecated": false, "preselect": false, @@ -473,7 +473,7 @@ "detail": "virtualNetworkRules", "documentation": { "kind": "markdown", - "value": "Type: `VirtualNetworkRule[]` \n" + "value": "Type: `VirtualNetworkRule[]` \nList of Virtual Network ACL rules configured for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -491,7 +491,7 @@ "detail": "writeLocations", "documentation": { "kind": "markdown", - "value": "Type: `Location[]` \nRead-only property \n" + "value": "Type: `Location[]` \nRead-only property \nAn array that contains the write location for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeIndexes_if.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeIndexes_if.json index 4bb3420a998..b2e201838fe 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeIndexes_if.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeIndexes_if.json @@ -5,7 +5,7 @@ "detail": "apiProperties", "documentation": { "kind": "markdown", - "value": "Type: `ApiProperties` \n" + "value": "Type: `ApiProperties` \n \n" }, "deprecated": false, "preselect": false, @@ -23,7 +23,7 @@ "detail": "backupPolicy", "documentation": { "kind": "markdown", - "value": "Type: `BackupPolicy` \n" + "value": "Type: `BackupPolicy` \nThe object representing the policy for taking backups on an account. \n" }, "deprecated": false, "preselect": false, @@ -41,7 +41,7 @@ "detail": "capabilities", "documentation": { "kind": "markdown", - "value": "Type: `Capability[]` \n" + "value": "Type: `Capability[]` \nList of Cosmos DB capabilities for the account \n" }, "deprecated": false, "preselect": false, @@ -59,7 +59,7 @@ "detail": "connectorOffer", "documentation": { "kind": "markdown", - "value": "Type: `'Small'` \n" + "value": "Type: `'Small'` \nThe cassandra connector offer type for the Cosmos DB database C* account. \n" }, "deprecated": false, "preselect": false, @@ -77,7 +77,7 @@ "detail": "consistencyPolicy", "documentation": { "kind": "markdown", - "value": "Type: `ConsistencyPolicy` \n" + "value": "Type: `ConsistencyPolicy` \nThe consistency policy for the Cosmos DB database account. \n" }, "deprecated": false, "preselect": false, @@ -95,7 +95,7 @@ "detail": "cors", "documentation": { "kind": "markdown", - "value": "Type: `CorsPolicy[]` \n" + "value": "Type: `CorsPolicy[]` \nThe CORS policy for the Cosmos DB database account. \n" }, "deprecated": false, "preselect": false, @@ -113,7 +113,7 @@ "detail": "createMode (Required)", "documentation": { "kind": "markdown", - "value": "Type: `'Default'` \n" + "value": "Type: `'Default'` \nProperties for non-restore Azure Cosmos DB database account requests. \n" }, "deprecated": false, "preselect": false, @@ -131,7 +131,7 @@ "detail": "databaseAccountOfferType (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe offer type for the database \n" }, "deprecated": false, "preselect": false, @@ -149,7 +149,7 @@ "detail": "disableKeyBasedMetadataWriteAccess", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nDisable write operations on metadata resources (databases, containers, throughput) via account keys \n" }, "deprecated": false, "preselect": false, @@ -167,7 +167,7 @@ "detail": "documentEndpoint", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nThe connection endpoint for the Cosmos DB database account. \n" }, "deprecated": false, "preselect": false, @@ -185,7 +185,7 @@ "detail": "enableAnalyticalStorage", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nFlag to indicate whether to enable storage analytics. \n" }, "deprecated": false, "preselect": false, @@ -203,7 +203,7 @@ "detail": "enableAutomaticFailover", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nEnables automatic failover of the write region in the rare event that the region is unavailable due to an outage. Automatic failover will result in a new write region for the account and is chosen based on the failover priorities configured for the account. \n" }, "deprecated": false, "preselect": false, @@ -221,7 +221,7 @@ "detail": "enableCassandraConnector", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nEnables the cassandra connector on the Cosmos DB C* account \n" }, "deprecated": false, "preselect": false, @@ -239,7 +239,7 @@ "detail": "enableFreeTier", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nFlag to indicate whether Free Tier is enabled. \n" }, "deprecated": false, "preselect": false, @@ -257,7 +257,7 @@ "detail": "enableMultipleWriteLocations", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nEnables the account to write in multiple locations \n" }, "deprecated": false, "preselect": false, @@ -275,7 +275,7 @@ "detail": "failoverPolicies", "documentation": { "kind": "markdown", - "value": "Type: `FailoverPolicy[]` \nRead-only property \n" + "value": "Type: `FailoverPolicy[]` \nRead-only property \nAn array that contains the regions ordered by their failover priorities. \n" }, "deprecated": false, "preselect": false, @@ -293,7 +293,7 @@ "detail": "instanceId", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nA unique identifier assigned to the database account \n" }, "deprecated": false, "preselect": false, @@ -311,7 +311,7 @@ "detail": "ipRules", "documentation": { "kind": "markdown", - "value": "Type: `IpAddressOrRange[]` \n" + "value": "Type: `IpAddressOrRange[]` \nArray of IpAddressOrRange objects. \n" }, "deprecated": false, "preselect": false, @@ -329,7 +329,7 @@ "detail": "isVirtualNetworkFilterEnabled", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nFlag to indicate whether to enable/disable Virtual Network ACL rules. \n" }, "deprecated": false, "preselect": false, @@ -347,7 +347,7 @@ "detail": "keyVaultKeyUri", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe URI of the key vault \n" }, "deprecated": false, "preselect": false, @@ -365,7 +365,7 @@ "detail": "locations (Required)", "documentation": { "kind": "markdown", - "value": "Type: `Location[]` \n" + "value": "Type: `Location[]` \nAn array that contains the georeplication locations enabled for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -383,7 +383,7 @@ "detail": "privateEndpointConnections", "documentation": { "kind": "markdown", - "value": "Type: `PrivateEndpointConnection[]` \nRead-only property \n" + "value": "Type: `PrivateEndpointConnection[]` \nRead-only property \nList of Private Endpoint Connections configured for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -401,7 +401,7 @@ "detail": "provisioningState", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nThe status of the Cosmos DB account at the time the operation was called. The status can be one of following. 'Creating' – the Cosmos DB account is being created. When an account is in Creating state, only properties that are specified as input for the Create Cosmos DB account operation are returned. 'Succeeded' – the Cosmos DB account is active for use. 'Updating' – the Cosmos DB account is being updated. 'Deleting' – the Cosmos DB account is being deleted. 'Failed' – the Cosmos DB account failed creation. 'DeletionFailed' – the Cosmos DB account deletion failed. \n" }, "deprecated": false, "preselect": false, @@ -419,7 +419,7 @@ "detail": "publicNetworkAccess", "documentation": { "kind": "markdown", - "value": "Type: `'Disabled' | 'Enabled'` \n" + "value": "Type: `'Disabled' | 'Enabled'` \nWhether requests from Public Network are allowed. \n" }, "deprecated": false, "preselect": false, @@ -437,7 +437,7 @@ "detail": "readLocations", "documentation": { "kind": "markdown", - "value": "Type: `Location[]` \nRead-only property \n" + "value": "Type: `Location[]` \nRead-only property \nAn array that contains of the read locations enabled for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -455,7 +455,7 @@ "detail": "restoreParameters", "documentation": { "kind": "markdown", - "value": "Type: `RestoreParameters` \nRead-only property \n" + "value": "Type: `RestoreParameters` \nRead-only property \nParameters to indicate the information about the restore. \n" }, "deprecated": false, "preselect": false, @@ -473,7 +473,7 @@ "detail": "virtualNetworkRules", "documentation": { "kind": "markdown", - "value": "Type: `VirtualNetworkRule[]` \n" + "value": "Type: `VirtualNetworkRule[]` \nList of Virtual Network ACL rules configured for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -491,7 +491,7 @@ "detail": "writeLocations", "documentation": { "kind": "markdown", - "value": "Type: `Location[]` \nRead-only property \n" + "value": "Type: `Location[]` \nRead-only property \nAn array that contains the write location for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeProperties.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeProperties.json index fdecb1ce948..2e9f9778994 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeProperties.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/defaultCreateModeProperties.json @@ -5,7 +5,7 @@ "detail": "apiProperties", "documentation": { "kind": "markdown", - "value": "Type: `ApiProperties` \n" + "value": "Type: `ApiProperties` \n \n" }, "deprecated": false, "preselect": false, @@ -26,7 +26,7 @@ "detail": "backupPolicy", "documentation": { "kind": "markdown", - "value": "Type: `BackupPolicy` \n" + "value": "Type: `BackupPolicy` \nThe object representing the policy for taking backups on an account. \n" }, "deprecated": false, "preselect": false, @@ -47,7 +47,7 @@ "detail": "capabilities", "documentation": { "kind": "markdown", - "value": "Type: `Capability[]` \n" + "value": "Type: `Capability[]` \nList of Cosmos DB capabilities for the account \n" }, "deprecated": false, "preselect": false, @@ -68,7 +68,7 @@ "detail": "connectorOffer", "documentation": { "kind": "markdown", - "value": "Type: `'Small'` \n" + "value": "Type: `'Small'` \nThe cassandra connector offer type for the Cosmos DB database C* account. \n" }, "deprecated": false, "preselect": false, @@ -89,7 +89,7 @@ "detail": "consistencyPolicy", "documentation": { "kind": "markdown", - "value": "Type: `ConsistencyPolicy` \n" + "value": "Type: `ConsistencyPolicy` \nThe consistency policy for the Cosmos DB database account. \n" }, "deprecated": false, "preselect": false, @@ -110,7 +110,7 @@ "detail": "cors", "documentation": { "kind": "markdown", - "value": "Type: `CorsPolicy[]` \n" + "value": "Type: `CorsPolicy[]` \nThe CORS policy for the Cosmos DB database account. \n" }, "deprecated": false, "preselect": false, @@ -131,7 +131,7 @@ "detail": "createMode (Required)", "documentation": { "kind": "markdown", - "value": "Type: `'Default'` \n" + "value": "Type: `'Default'` \nProperties for non-restore Azure Cosmos DB database account requests. \n" }, "deprecated": false, "preselect": false, @@ -152,7 +152,7 @@ "detail": "databaseAccountOfferType (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe offer type for the database \n" }, "deprecated": false, "preselect": false, @@ -173,7 +173,7 @@ "detail": "disableKeyBasedMetadataWriteAccess", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nDisable write operations on metadata resources (databases, containers, throughput) via account keys \n" }, "deprecated": false, "preselect": false, @@ -194,7 +194,7 @@ "detail": "documentEndpoint", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nThe connection endpoint for the Cosmos DB database account. \n" }, "deprecated": false, "preselect": false, @@ -215,7 +215,7 @@ "detail": "enableAnalyticalStorage", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nFlag to indicate whether to enable storage analytics. \n" }, "deprecated": false, "preselect": false, @@ -236,7 +236,7 @@ "detail": "enableAutomaticFailover", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nEnables automatic failover of the write region in the rare event that the region is unavailable due to an outage. Automatic failover will result in a new write region for the account and is chosen based on the failover priorities configured for the account. \n" }, "deprecated": false, "preselect": false, @@ -257,7 +257,7 @@ "detail": "enableCassandraConnector", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nEnables the cassandra connector on the Cosmos DB C* account \n" }, "deprecated": false, "preselect": false, @@ -278,7 +278,7 @@ "detail": "enableFreeTier", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nFlag to indicate whether Free Tier is enabled. \n" }, "deprecated": false, "preselect": false, @@ -299,7 +299,7 @@ "detail": "enableMultipleWriteLocations", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nEnables the account to write in multiple locations \n" }, "deprecated": false, "preselect": false, @@ -320,7 +320,7 @@ "detail": "failoverPolicies", "documentation": { "kind": "markdown", - "value": "Type: `FailoverPolicy[]` \nRead-only property \n" + "value": "Type: `FailoverPolicy[]` \nRead-only property \nAn array that contains the regions ordered by their failover priorities. \n" }, "deprecated": false, "preselect": false, @@ -341,7 +341,7 @@ "detail": "instanceId", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nA unique identifier assigned to the database account \n" }, "deprecated": false, "preselect": false, @@ -362,7 +362,7 @@ "detail": "ipRules", "documentation": { "kind": "markdown", - "value": "Type: `IpAddressOrRange[]` \n" + "value": "Type: `IpAddressOrRange[]` \nArray of IpAddressOrRange objects. \n" }, "deprecated": false, "preselect": false, @@ -383,7 +383,7 @@ "detail": "isVirtualNetworkFilterEnabled", "documentation": { "kind": "markdown", - "value": "Type: `bool` \n" + "value": "Type: `bool` \nFlag to indicate whether to enable/disable Virtual Network ACL rules. \n" }, "deprecated": false, "preselect": false, @@ -404,7 +404,7 @@ "detail": "keyVaultKeyUri", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe URI of the key vault \n" }, "deprecated": false, "preselect": false, @@ -425,7 +425,7 @@ "detail": "locations (Required)", "documentation": { "kind": "markdown", - "value": "Type: `Location[]` \n" + "value": "Type: `Location[]` \nAn array that contains the georeplication locations enabled for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -446,7 +446,7 @@ "detail": "privateEndpointConnections", "documentation": { "kind": "markdown", - "value": "Type: `PrivateEndpointConnection[]` \nRead-only property \n" + "value": "Type: `PrivateEndpointConnection[]` \nRead-only property \nList of Private Endpoint Connections configured for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -467,7 +467,7 @@ "detail": "provisioningState", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nThe status of the Cosmos DB account at the time the operation was called. The status can be one of following. 'Creating' – the Cosmos DB account is being created. When an account is in Creating state, only properties that are specified as input for the Create Cosmos DB account operation are returned. 'Succeeded' – the Cosmos DB account is active for use. 'Updating' – the Cosmos DB account is being updated. 'Deleting' – the Cosmos DB account is being deleted. 'Failed' – the Cosmos DB account failed creation. 'DeletionFailed' – the Cosmos DB account deletion failed. \n" }, "deprecated": false, "preselect": false, @@ -488,7 +488,7 @@ "detail": "publicNetworkAccess", "documentation": { "kind": "markdown", - "value": "Type: `'Disabled' | 'Enabled'` \n" + "value": "Type: `'Disabled' | 'Enabled'` \nWhether requests from Public Network are allowed. \n" }, "deprecated": false, "preselect": false, @@ -509,7 +509,7 @@ "detail": "readLocations", "documentation": { "kind": "markdown", - "value": "Type: `Location[]` \nRead-only property \n" + "value": "Type: `Location[]` \nRead-only property \nAn array that contains of the read locations enabled for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -530,7 +530,7 @@ "detail": "restoreParameters", "documentation": { "kind": "markdown", - "value": "Type: `RestoreParameters` \nRead-only property \n" + "value": "Type: `RestoreParameters` \nRead-only property \nParameters to indicate the information about the restore. \n" }, "deprecated": false, "preselect": false, @@ -551,7 +551,7 @@ "detail": "virtualNetworkRules", "documentation": { "kind": "markdown", - "value": "Type: `VirtualNetworkRule[]` \n" + "value": "Type: `VirtualNetworkRule[]` \nList of Virtual Network ACL rules configured for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, @@ -572,7 +572,7 @@ "detail": "writeLocations", "documentation": { "kind": "markdown", - "value": "Type: `Location[]` \nRead-only property \n" + "value": "Type: `Location[]` \nRead-only property \nAn array that contains the write location for the Cosmos DB account. \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/delegationProperties.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/delegationProperties.json index 5225c9412ba..d40894036b7 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/delegationProperties.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/delegationProperties.json @@ -5,7 +5,7 @@ "detail": "id", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nResource ID. \n" }, "deprecated": false, "preselect": false, @@ -26,7 +26,7 @@ "detail": "name", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe name of the resource that is unique within a subnet. This name can be used to access the resource. \n" }, "deprecated": false, "preselect": false, @@ -47,7 +47,7 @@ "detail": "properties", "documentation": { "kind": "markdown", - "value": "Type: `ServiceDelegationPropertiesFormat` \n" + "value": "Type: `ServiceDelegationPropertiesFormat` \nProperties of a service delegation. \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/deploymentScriptCliProperties.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/deploymentScriptCliProperties.json index 3b063e3d8e9..94cd9ef2390 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/deploymentScriptCliProperties.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/deploymentScriptCliProperties.json @@ -5,7 +5,7 @@ "detail": "arguments", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nCommand line arguments to pass to the script. Arguments are separated by spaces. ex: -Name blue* -Location 'West US 2' \n" }, "deprecated": false, "preselect": false, @@ -26,7 +26,7 @@ "detail": "azCliVersion (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nAzure CLI module version to be used. \n" }, "deprecated": false, "preselect": false, @@ -47,7 +47,7 @@ "detail": "cleanupPreference", "documentation": { "kind": "markdown", - "value": "Type: `'Always' | 'OnExpiration' | 'OnSuccess'` \n" + "value": "Type: `'Always' | 'OnExpiration' | 'OnSuccess'` \nThe clean up preference when the script execution gets in a terminal state. Default setting is 'Always'. \n" }, "deprecated": false, "preselect": false, @@ -68,7 +68,7 @@ "detail": "containerSettings", "documentation": { "kind": "markdown", - "value": "Type: `ContainerConfiguration` \n" + "value": "Type: `ContainerConfiguration` \nSettings to customize ACI container instance. \n" }, "deprecated": false, "preselect": false, @@ -89,7 +89,7 @@ "detail": "environmentVariables", "documentation": { "kind": "markdown", - "value": "Type: `EnvironmentVariable[]` \n" + "value": "Type: `EnvironmentVariable[]` \nThe environment variables to pass over to the script. \n" }, "deprecated": false, "preselect": false, @@ -110,7 +110,7 @@ "detail": "forceUpdateTag", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nGets or sets how the deployment script should be forced to execute even if the script resource has not changed. Can be current time stamp or a GUID. \n" }, "deprecated": false, "preselect": false, @@ -131,7 +131,7 @@ "detail": "primaryScriptUri", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nUri for the script. This is the entry point for the external script. \n" }, "deprecated": false, "preselect": false, @@ -152,7 +152,7 @@ "detail": "retentionInterval (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nInterval for which the service retains the script resource after it reaches a terminal state. Resource will be deleted when this duration expires. Duration is based on ISO 8601 pattern (for example P1D means one day). \n" }, "deprecated": false, "preselect": false, @@ -173,7 +173,7 @@ "detail": "scriptContent", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nScript body. \n" }, "deprecated": false, "preselect": false, @@ -194,7 +194,7 @@ "detail": "storageAccountSettings", "documentation": { "kind": "markdown", - "value": "Type: `StorageAccountConfiguration` \n" + "value": "Type: `StorageAccountConfiguration` \nSettings to use an existing storage account. Valid storage account kinds are: Storage, StorageV2 and FileStorage \n" }, "deprecated": false, "preselect": false, @@ -215,7 +215,7 @@ "detail": "supportingScriptUris", "documentation": { "kind": "markdown", - "value": "Type: `string[]` \n" + "value": "Type: `string[]` \nSupporting files for the external script. \n" }, "deprecated": false, "preselect": false, @@ -236,7 +236,7 @@ "detail": "timeout", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nMaximum allowed script execution time specified in ISO 8601 format. Default value is P1D \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/deploymentScriptCliPropertiesMinusSpecified.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/deploymentScriptCliPropertiesMinusSpecified.json index 7f07203cbd6..f320b1875ae 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/deploymentScriptCliPropertiesMinusSpecified.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/deploymentScriptCliPropertiesMinusSpecified.json @@ -5,7 +5,7 @@ "detail": "arguments", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nCommand line arguments to pass to the script. Arguments are separated by spaces. ex: -Name blue* -Location 'West US 2' \n" }, "deprecated": false, "preselect": false, @@ -26,7 +26,7 @@ "detail": "containerSettings", "documentation": { "kind": "markdown", - "value": "Type: `ContainerConfiguration` \n" + "value": "Type: `ContainerConfiguration` \nSettings to customize ACI container instance. \n" }, "deprecated": false, "preselect": false, @@ -47,7 +47,7 @@ "detail": "forceUpdateTag", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nGets or sets how the deployment script should be forced to execute even if the script resource has not changed. Can be current time stamp or a GUID. \n" }, "deprecated": false, "preselect": false, @@ -68,7 +68,7 @@ "detail": "primaryScriptUri", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nUri for the script. This is the entry point for the external script. \n" }, "deprecated": false, "preselect": false, @@ -89,7 +89,7 @@ "detail": "scriptContent", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nScript body. \n" }, "deprecated": false, "preselect": false, @@ -110,7 +110,7 @@ "detail": "timeout", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nMaximum allowed script execution time specified in ISO 8601 format. Default value is P1D \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/deploymentScriptPSProperties.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/deploymentScriptPSProperties.json index 8b3ddeefdb7..1df1f1f8cf7 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/deploymentScriptPSProperties.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/deploymentScriptPSProperties.json @@ -5,7 +5,7 @@ "detail": "arguments", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nCommand line arguments to pass to the script. Arguments are separated by spaces. ex: -Name blue* -Location 'West US 2' \n" }, "deprecated": false, "preselect": false, @@ -26,7 +26,7 @@ "detail": "azPowerShellVersion (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nAzure PowerShell module version to be used. \n" }, "deprecated": false, "preselect": false, @@ -47,7 +47,7 @@ "detail": "cleanupPreference", "documentation": { "kind": "markdown", - "value": "Type: `'Always' | 'OnExpiration' | 'OnSuccess'` \n" + "value": "Type: `'Always' | 'OnExpiration' | 'OnSuccess'` \nThe clean up preference when the script execution gets in a terminal state. Default setting is 'Always'. \n" }, "deprecated": false, "preselect": false, @@ -68,7 +68,7 @@ "detail": "containerSettings", "documentation": { "kind": "markdown", - "value": "Type: `ContainerConfiguration` \n" + "value": "Type: `ContainerConfiguration` \nSettings to customize ACI container instance. \n" }, "deprecated": false, "preselect": false, @@ -89,7 +89,7 @@ "detail": "environmentVariables", "documentation": { "kind": "markdown", - "value": "Type: `EnvironmentVariable[]` \n" + "value": "Type: `EnvironmentVariable[]` \nThe environment variables to pass over to the script. \n" }, "deprecated": false, "preselect": false, @@ -110,7 +110,7 @@ "detail": "forceUpdateTag", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nGets or sets how the deployment script should be forced to execute even if the script resource has not changed. Can be current time stamp or a GUID. \n" }, "deprecated": false, "preselect": false, @@ -131,7 +131,7 @@ "detail": "primaryScriptUri", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nUri for the script. This is the entry point for the external script. \n" }, "deprecated": false, "preselect": false, @@ -152,7 +152,7 @@ "detail": "retentionInterval (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nInterval for which the service retains the script resource after it reaches a terminal state. Resource will be deleted when this duration expires. Duration is based on ISO 8601 pattern (for example P1D means one day). \n" }, "deprecated": false, "preselect": false, @@ -173,7 +173,7 @@ "detail": "scriptContent", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nScript body. \n" }, "deprecated": false, "preselect": false, @@ -194,7 +194,7 @@ "detail": "storageAccountSettings", "documentation": { "kind": "markdown", - "value": "Type: `StorageAccountConfiguration` \n" + "value": "Type: `StorageAccountConfiguration` \nSettings to use an existing storage account. Valid storage account kinds are: Storage, StorageV2 and FileStorage \n" }, "deprecated": false, "preselect": false, @@ -215,7 +215,7 @@ "detail": "supportingScriptUris", "documentation": { "kind": "markdown", - "value": "Type: `string[]` \n" + "value": "Type: `string[]` \nSupporting files for the external script. \n" }, "deprecated": false, "preselect": false, @@ -236,7 +236,7 @@ "detail": "timeout", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nMaximum allowed script execution time specified in ISO 8601 format. Default value is P1D \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/deploymentScriptTopLevel.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/deploymentScriptTopLevel.json index fb6f08fa3e0..a94206feadb 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/deploymentScriptTopLevel.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/deploymentScriptTopLevel.json @@ -26,7 +26,7 @@ "detail": "identity", "documentation": { "kind": "markdown", - "value": "Type: `ManagedServiceIdentity` \n" + "value": "Type: `ManagedServiceIdentity` \nManaged identity generic object. \n" }, "deprecated": false, "preselect": false, @@ -47,7 +47,7 @@ "detail": "location (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe location of the ACI and the storage account for the deployment script. \n" }, "deprecated": false, "preselect": false, @@ -68,7 +68,7 @@ "detail": "name (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe resource name \n" }, "deprecated": false, "preselect": false, @@ -139,7 +139,7 @@ "detail": "tags", "documentation": { "kind": "markdown", - "value": "Type: `Dictionary` \n" + "value": "Type: `Dictionary` \nResource tags. \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/environmentVariableProperties.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/environmentVariableProperties.json index 41510f6fd06..c849a751c96 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/environmentVariableProperties.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/environmentVariableProperties.json @@ -5,7 +5,7 @@ "detail": "name (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe name of the environment variable. \n" }, "deprecated": false, "preselect": false, @@ -26,7 +26,7 @@ "detail": "secureValue", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe value of the secure environment variable. \n" }, "deprecated": false, "preselect": false, @@ -47,7 +47,7 @@ "detail": "value", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe value of the environment variable. \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/powershellPropertyAccess.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/powershellPropertyAccess.json index 6c5a381db6a..fad03ce694f 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/powershellPropertyAccess.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/powershellPropertyAccess.json @@ -5,7 +5,7 @@ "detail": "arguments", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nCommand line arguments to pass to the script. Arguments are separated by spaces. ex: -Name blue* -Location 'West US 2' \n" }, "deprecated": false, "preselect": false, @@ -26,7 +26,7 @@ "detail": "azPowerShellVersion (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nAzure PowerShell module version to be used. \n" }, "deprecated": false, "preselect": false, @@ -47,7 +47,7 @@ "detail": "cleanupPreference", "documentation": { "kind": "markdown", - "value": "Type: `'Always' | 'OnExpiration' | 'OnSuccess'` \n" + "value": "Type: `'Always' | 'OnExpiration' | 'OnSuccess'` \nThe clean up preference when the script execution gets in a terminal state. Default setting is 'Always'. \n" }, "deprecated": false, "preselect": false, @@ -68,7 +68,7 @@ "detail": "containerSettings", "documentation": { "kind": "markdown", - "value": "Type: `ContainerConfiguration` \n" + "value": "Type: `ContainerConfiguration` \nSettings to customize ACI container instance. \n" }, "deprecated": false, "preselect": false, @@ -89,7 +89,7 @@ "detail": "environmentVariables", "documentation": { "kind": "markdown", - "value": "Type: `EnvironmentVariable[]` \n" + "value": "Type: `EnvironmentVariable[]` \nThe environment variables to pass over to the script. \n" }, "deprecated": false, "preselect": false, @@ -110,7 +110,7 @@ "detail": "forceUpdateTag", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nGets or sets how the deployment script should be forced to execute even if the script resource has not changed. Can be current time stamp or a GUID. \n" }, "deprecated": false, "preselect": false, @@ -131,7 +131,7 @@ "detail": "outputs", "documentation": { "kind": "markdown", - "value": "Type: `Dictionary` \nRead-only property \n" + "value": "Type: `Dictionary` \nRead-only property \nList of script outputs. \n" }, "deprecated": false, "preselect": false, @@ -152,7 +152,7 @@ "detail": "primaryScriptUri", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nUri for the script. This is the entry point for the external script. \n" }, "deprecated": false, "preselect": false, @@ -173,7 +173,7 @@ "detail": "provisioningState", "documentation": { "kind": "markdown", - "value": "Type: `'Canceled' | 'Creating' | 'Failed' | 'ProvisioningResources' | 'Running' | 'Succeeded'` \nRead-only property \n" + "value": "Type: `'Canceled' | 'Creating' | 'Failed' | 'ProvisioningResources' | 'Running' | 'Succeeded'` \nRead-only property \nState of the script execution. This only appears in the response. \n" }, "deprecated": false, "preselect": false, @@ -194,7 +194,7 @@ "detail": "retentionInterval (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nInterval for which the service retains the script resource after it reaches a terminal state. Resource will be deleted when this duration expires. Duration is based on ISO 8601 pattern (for example P1D means one day). \n" }, "deprecated": false, "preselect": false, @@ -215,7 +215,7 @@ "detail": "scriptContent", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nScript body. \n" }, "deprecated": false, "preselect": false, @@ -236,7 +236,7 @@ "detail": "status", "documentation": { "kind": "markdown", - "value": "Type: `ScriptStatus` \nRead-only property \n" + "value": "Type: `ScriptStatus` \nRead-only property \nGeneric object modeling results of script execution. \n" }, "deprecated": false, "preselect": false, @@ -257,7 +257,7 @@ "detail": "storageAccountSettings", "documentation": { "kind": "markdown", - "value": "Type: `StorageAccountConfiguration` \n" + "value": "Type: `StorageAccountConfiguration` \nSettings to use an existing storage account. Valid storage account kinds are: Storage, StorageV2 and FileStorage \n" }, "deprecated": false, "preselect": false, @@ -278,7 +278,7 @@ "detail": "supportingScriptUris", "documentation": { "kind": "markdown", - "value": "Type: `string[]` \n" + "value": "Type: `string[]` \nSupporting files for the external script. \n" }, "deprecated": false, "preselect": false, @@ -299,7 +299,7 @@ "detail": "timeout", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nMaximum allowed script execution time specified in ISO 8601 format. Default value is P1D \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/subnetPropertiesMinusProperties.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/subnetPropertiesMinusProperties.json index 4dc16f35691..383f88fdd51 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/subnetPropertiesMinusProperties.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/subnetPropertiesMinusProperties.json @@ -5,7 +5,7 @@ "detail": "id", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nResource ID. \n" }, "deprecated": false, "preselect": false, @@ -26,7 +26,7 @@ "detail": "name", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe name of the resource that is unique within a resource group. This name can be used to access the resource. \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/topLevelProperties.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/topLevelProperties.json index 15bff961b8e..8d568ff5d7c 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/topLevelProperties.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/topLevelProperties.json @@ -26,7 +26,7 @@ "detail": "extendedLocation", "documentation": { "kind": "markdown", - "value": "Type: `ExtendedLocation` \n" + "value": "Type: `ExtendedLocation` \nThe complex type of the extended location. \n" }, "deprecated": false, "preselect": false, @@ -47,7 +47,7 @@ "detail": "identity", "documentation": { "kind": "markdown", - "value": "Type: `Identity` \n" + "value": "Type: `Identity` \nIdentity for the resource. \n" }, "deprecated": false, "preselect": false, @@ -68,7 +68,7 @@ "detail": "kind (Required)", "documentation": { "kind": "markdown", - "value": "Type: `'BlobStorage' | 'BlockBlobStorage' | 'FileStorage' | 'Storage' | 'StorageV2'` \n" + "value": "Type: `'BlobStorage' | 'BlockBlobStorage' | 'FileStorage' | 'Storage' | 'StorageV2'` \nRequired. Indicates the type of storage account. \n" }, "deprecated": false, "preselect": false, @@ -89,7 +89,7 @@ "detail": "location (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nRequired. Gets or sets the location of the resource. This will be one of the supported and registered Azure Geo Regions (e.g. West US, East US, Southeast Asia, etc.). The geo region of a resource cannot be changed once it is created, but if an identical geo region is specified on update, the request will succeed. \n" }, "deprecated": false, "preselect": false, @@ -110,7 +110,7 @@ "detail": "name (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nThe resource name \n" }, "deprecated": false, "preselect": false, @@ -131,7 +131,7 @@ "detail": "properties", "documentation": { "kind": "markdown", - "value": "Type: `StorageAccountPropertiesCreateParameters` \n" + "value": "Type: `StorageAccountPropertiesCreateParameters` \nThe parameters used to create the storage account. \n" }, "deprecated": false, "preselect": false, @@ -202,7 +202,7 @@ "detail": "sku (Required)", "documentation": { "kind": "markdown", - "value": "Type: `Sku` \n" + "value": "Type: `Sku` \nThe SKU of the storage account. \n" }, "deprecated": false, "preselect": false, @@ -223,7 +223,7 @@ "detail": "tags", "documentation": { "kind": "markdown", - "value": "Type: `Dictionary` \n" + "value": "Type: `Dictionary` \nGets or sets a list of key value pairs that describe the resource. These tags can be used for viewing and grouping this resource (across resource groups). A maximum of 15 tags can be provided for a resource. Each tag must have a key with a length no greater than 128 characters and a value with a length no greater than 256 characters. \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/topLevelPropertiesMinusName.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/topLevelPropertiesMinusName.json index df91644a73f..8feaa2cbab7 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/topLevelPropertiesMinusName.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/topLevelPropertiesMinusName.json @@ -26,7 +26,7 @@ "detail": "extendedLocation", "documentation": { "kind": "markdown", - "value": "Type: `ExtendedLocation` \n" + "value": "Type: `ExtendedLocation` \nThe complex type of the extended location. \n" }, "deprecated": false, "preselect": false, @@ -47,7 +47,7 @@ "detail": "identity", "documentation": { "kind": "markdown", - "value": "Type: `Identity` \n" + "value": "Type: `Identity` \nIdentity for the resource. \n" }, "deprecated": false, "preselect": false, @@ -68,7 +68,7 @@ "detail": "kind (Required)", "documentation": { "kind": "markdown", - "value": "Type: `'BlobStorage' | 'BlockBlobStorage' | 'FileStorage' | 'Storage' | 'StorageV2'` \n" + "value": "Type: `'BlobStorage' | 'BlockBlobStorage' | 'FileStorage' | 'Storage' | 'StorageV2'` \nRequired. Indicates the type of storage account. \n" }, "deprecated": false, "preselect": false, @@ -89,7 +89,7 @@ "detail": "location (Required)", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nRequired. Gets or sets the location of the resource. This will be one of the supported and registered Azure Geo Regions (e.g. West US, East US, Southeast Asia, etc.). The geo region of a resource cannot be changed once it is created, but if an identical geo region is specified on update, the request will succeed. \n" }, "deprecated": false, "preselect": false, @@ -110,7 +110,7 @@ "detail": "properties", "documentation": { "kind": "markdown", - "value": "Type: `StorageAccountPropertiesCreateParameters` \n" + "value": "Type: `StorageAccountPropertiesCreateParameters` \nThe parameters used to create the storage account. \n" }, "deprecated": false, "preselect": false, @@ -181,7 +181,7 @@ "detail": "sku (Required)", "documentation": { "kind": "markdown", - "value": "Type: `Sku` \n" + "value": "Type: `Sku` \nThe SKU of the storage account. \n" }, "deprecated": false, "preselect": false, @@ -202,7 +202,7 @@ "detail": "tags", "documentation": { "kind": "markdown", - "value": "Type: `Dictionary` \n" + "value": "Type: `Dictionary` \nGets or sets a list of key value pairs that describe the resource. These tags can be used for viewing and grouping this resource (across resource groups). A maximum of 15 tags can be provided for a resource. Each tag must have a key with a length no greater than 128 characters and a value with a length no greater than 256 characters. \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/vmProperties.json b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/vmProperties.json index 3bafc33e979..0ce1e1ff67d 100644 --- a/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/vmProperties.json +++ b/src/Bicep.Core.Samples/Files/InvalidResources_CRLF/Completions/vmProperties.json @@ -5,7 +5,7 @@ "detail": "additionalCapabilities", "documentation": { "kind": "markdown", - "value": "Type: `AdditionalCapabilities` \n" + "value": "Type: `AdditionalCapabilities` \nEnables or disables a capability on the virtual machine or virtual machine scale set. \n" }, "deprecated": false, "preselect": false, @@ -26,7 +26,7 @@ "detail": "availabilitySet", "documentation": { "kind": "markdown", - "value": "Type: `SubResource` \n" + "value": "Type: `SubResource` \n \n" }, "deprecated": false, "preselect": false, @@ -47,7 +47,7 @@ "detail": "billingProfile", "documentation": { "kind": "markdown", - "value": "Type: `BillingProfile` \n" + "value": "Type: `BillingProfile` \nSpecifies the billing related details of a Azure Spot VM or VMSS.

Minimum api-version: 2019-03-01. \n" }, "deprecated": false, "preselect": false, @@ -68,7 +68,7 @@ "detail": "diagnosticsProfile", "documentation": { "kind": "markdown", - "value": "Type: `DiagnosticsProfile` \n" + "value": "Type: `DiagnosticsProfile` \nSpecifies the boot diagnostic settings state.

Minimum api-version: 2015-06-15. \n" }, "deprecated": false, "preselect": false, @@ -89,7 +89,7 @@ "detail": "evictionPolicy", "documentation": { "kind": "markdown", - "value": "Type: `'Deallocate' | 'Delete'` \n" + "value": "Type: `'Deallocate' | 'Delete'` \nSpecifies the eviction policy for the Azure Spot virtual machine and Azure Spot scale set.

For Azure Spot virtual machines, both 'Deallocate' and 'Delete' are supported and the minimum api-version is 2019-03-01.

For Azure Spot scale sets, both 'Deallocate' and 'Delete' are supported and the minimum api-version is 2017-10-30-preview. \n" }, "deprecated": false, "preselect": false, @@ -110,7 +110,7 @@ "detail": "extensionsTimeBudget", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nSpecifies the time alloted for all extensions to start. The time duration should be between 15 minutes and 120 minutes (inclusive) and should be specified in ISO 8601 format. The default value is 90 minutes (PT1H30M).

Minimum api-version: 2020-06-01 \n" }, "deprecated": false, "preselect": false, @@ -131,7 +131,7 @@ "detail": "hardwareProfile", "documentation": { "kind": "markdown", - "value": "Type: `HardwareProfile` \n" + "value": "Type: `HardwareProfile` \nSpecifies the hardware settings for the virtual machine. \n" }, "deprecated": false, "preselect": false, @@ -152,7 +152,7 @@ "detail": "host", "documentation": { "kind": "markdown", - "value": "Type: `SubResource` \n" + "value": "Type: `SubResource` \n \n" }, "deprecated": false, "preselect": false, @@ -173,7 +173,7 @@ "detail": "hostGroup", "documentation": { "kind": "markdown", - "value": "Type: `SubResource` \n" + "value": "Type: `SubResource` \n \n" }, "deprecated": false, "preselect": false, @@ -194,7 +194,7 @@ "detail": "instanceView", "documentation": { "kind": "markdown", - "value": "Type: `VirtualMachineInstanceView` \nRead-only property \n" + "value": "Type: `VirtualMachineInstanceView` \nRead-only property \nThe instance view of a virtual machine. \n" }, "deprecated": false, "preselect": false, @@ -215,7 +215,7 @@ "detail": "licenseType", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nSpecifies that the image or disk that is being used was licensed on-premises.

Possible values for Windows Server operating system are:

Windows_Client

Windows_Server

Possible values for Linux Server operating system are:

RHEL_BYOS (for RHEL)

SLES_BYOS (for SUSE)

For more information, see [Azure Hybrid Use Benefit for Windows Server](https://docs.microsoft.com/azure/virtual-machines/windows/hybrid-use-benefit-licensing)

[Azure Hybrid Use Benefit for Linux Server](https://docs.microsoft.com/azure/virtual-machines/linux/azure-hybrid-benefit-linux)

Minimum api-version: 2015-06-15 \n" }, "deprecated": false, "preselect": false, @@ -236,7 +236,7 @@ "detail": "networkProfile", "documentation": { "kind": "markdown", - "value": "Type: `NetworkProfile` \n" + "value": "Type: `NetworkProfile` \nSpecifies the network interfaces of the virtual machine. \n" }, "deprecated": false, "preselect": false, @@ -257,7 +257,7 @@ "detail": "osProfile", "documentation": { "kind": "markdown", - "value": "Type: `OSProfile` \n" + "value": "Type: `OSProfile` \nSpecifies the operating system settings for the virtual machine. Some of the settings cannot be changed once VM is provisioned. \n" }, "deprecated": false, "preselect": false, @@ -278,7 +278,7 @@ "detail": "priority", "documentation": { "kind": "markdown", - "value": "Type: `'Low' | 'Regular' | 'Spot'` \n" + "value": "Type: `'Low' | 'Regular' | 'Spot'` \nSpecifies the priority for the virtual machine.

Minimum api-version: 2019-03-01. \n" }, "deprecated": false, "preselect": false, @@ -299,7 +299,7 @@ "detail": "provisioningState", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nThe provisioning state, which only appears in the response. \n" }, "deprecated": false, "preselect": false, @@ -320,7 +320,7 @@ "detail": "proximityPlacementGroup", "documentation": { "kind": "markdown", - "value": "Type: `SubResource` \n" + "value": "Type: `SubResource` \n \n" }, "deprecated": false, "preselect": false, @@ -341,7 +341,7 @@ "detail": "securityProfile", "documentation": { "kind": "markdown", - "value": "Type: `SecurityProfile` \n" + "value": "Type: `SecurityProfile` \nSpecifies the Security profile settings for the virtual machine or virtual machine scale set. \n" }, "deprecated": false, "preselect": false, @@ -362,7 +362,7 @@ "detail": "storageProfile", "documentation": { "kind": "markdown", - "value": "Type: `StorageProfile` \n" + "value": "Type: `StorageProfile` \nSpecifies the storage settings for the virtual machine disks. \n" }, "deprecated": false, "preselect": false, @@ -383,7 +383,7 @@ "detail": "virtualMachineScaleSet", "documentation": { "kind": "markdown", - "value": "Type: `SubResource` \n" + "value": "Type: `SubResource` \n \n" }, "deprecated": false, "preselect": false, @@ -404,7 +404,7 @@ "detail": "vmId", "documentation": { "kind": "markdown", - "value": "Type: `string` \nRead-only property \n" + "value": "Type: `string` \nRead-only property \nSpecifies the VM unique ID which is a 128-bits identifier that is encoded and stored in all Azure IaaS VMs SMBIOS and can be read using platform BIOS commands. \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.Samples/Files/Resources_CRLF/Completions/subnetIdAndProperties.json b/src/Bicep.Core.Samples/Files/Resources_CRLF/Completions/subnetIdAndProperties.json index 28c7beb3db1..ff10fa16f9e 100644 --- a/src/Bicep.Core.Samples/Files/Resources_CRLF/Completions/subnetIdAndProperties.json +++ b/src/Bicep.Core.Samples/Files/Resources_CRLF/Completions/subnetIdAndProperties.json @@ -5,7 +5,7 @@ "detail": "id", "documentation": { "kind": "markdown", - "value": "Type: `string` \n" + "value": "Type: `string` \nResource ID. \n" }, "deprecated": false, "preselect": false, @@ -26,7 +26,7 @@ "detail": "properties", "documentation": { "kind": "markdown", - "value": "Type: `SubnetPropertiesFormat` \n" + "value": "Type: `SubnetPropertiesFormat` \nProperties of the subnet. \n" }, "deprecated": false, "preselect": false, diff --git a/src/Bicep.Core.UnitTests/Assertions/AssertionScopeExtensions.cs b/src/Bicep.Core.UnitTests/Assertions/AssertionScopeExtensions.cs new file mode 100644 index 00000000000..cf8c9f54c53 --- /dev/null +++ b/src/Bicep.Core.UnitTests/Assertions/AssertionScopeExtensions.cs @@ -0,0 +1,47 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.Linq; +using Bicep.Core.Diagnostics; +using Bicep.Core.Extensions; +using Bicep.Core.Parsing; +using Bicep.Core.Syntax; +using Bicep.Core.UnitTests.Utils; +using FluentAssertions.Execution; + +namespace Bicep.Core.UnitTests.Assertions +{ + public static class AssertionScopeExtensions + { + /// + /// Prints the program syntax with line numbers and a cursor if a test fails in the given assertion scope. + /// + public static AssertionScope WithVisualCursor(this AssertionScope assertionScope, SyntaxTree syntaxTree, IPositionable cursorPosition) + => WithAnnotatedSource( + assertionScope, + syntaxTree, + "cursor info", + new PrintHelper.Annotation(cursorPosition.Span, "cursor").AsEnumerable()); + + /// + /// Prints the program syntax with line numbers and diagnostics if a test fails in the given assertion scope. + /// + public static AssertionScope WithVisualDiagnostics(this AssertionScope assertionScope, SyntaxTree syntaxTree, IEnumerable diagnostics) + => WithAnnotatedSource( + assertionScope, + syntaxTree, + "diagnostics", + diagnostics.Select(x => new PrintHelper.Annotation(x.Span, $"[{x.Code} ({x.Level})] {x.Message}"))); + + public static AssertionScope WithAnnotatedSource(AssertionScope assertionScope, SyntaxTree syntaxTree, string contextName, IEnumerable annotations) + { + // TODO: figure out how to set this only on failure, rather than always calculating it + assertionScope.AddReportable( + contextName, + PrintHelper.PrintWithAnnotations(syntaxTree, annotations, 1, true)); + + return assertionScope; + } + } +} diff --git a/src/Bicep.Core.UnitTests/Assertions/DiagnosticAssertions.cs b/src/Bicep.Core.UnitTests/Assertions/DiagnosticAssertions.cs index b45541e41b2..35628495e5b 100644 --- a/src/Bicep.Core.UnitTests/Assertions/DiagnosticAssertions.cs +++ b/src/Bicep.Core.UnitTests/Assertions/DiagnosticAssertions.cs @@ -53,14 +53,8 @@ public DiagnosticAssertions(Diagnostic diagnostic) public static void DoWithDiagnosticAnnotations(SyntaxTree syntaxTree, IEnumerable diagnostics, Action> action) { - using (var scope = new AssertionScope()) + using (new AssertionScope().WithVisualDiagnostics(syntaxTree, diagnostics)) { - scope.AddReportable("diagnostics", PrintHelper.PrintWithAnnotations( - syntaxTree, - diagnostics.Select(x => new PrintHelper.Annotation(x.Span, $"[{x.Code} ({x.Level})] {x.Message}")), - 1, - true)); - action(diagnostics); } } diff --git a/src/Bicep.Core.UnitTests/TypeSystem/Az/AzResourceTypeProviderTests.cs b/src/Bicep.Core.UnitTests/TypeSystem/Az/AzResourceTypeProviderTests.cs index 5d66804e193..e2083b6b64b 100644 --- a/src/Bicep.Core.UnitTests/TypeSystem/Az/AzResourceTypeProviderTests.cs +++ b/src/Bicep.Core.UnitTests/TypeSystem/Az/AzResourceTypeProviderTests.cs @@ -68,10 +68,8 @@ public void AzResourceTypeProvider_can_list_all_types_without_throwing() [TestMethod] public void AzResourceTypeProvider_should_warn_for_missing_resource_types() { - - var typeLoader = CreateMockTypeLoader(ResourceTypeReference.Parse("Mock.Rp/mockType@2020-01-01")); Compilation createCompilation(string program) - => new Compilation(new AzResourceTypeProvider(typeLoader), SyntaxTreeGroupingFactory.CreateFromText(program)); + => new Compilation(BuiltInTestTypes.Create(), SyntaxTreeGroupingFactory.CreateFromText(program)); // Missing top-level properties - should be an error var compilation = createCompilation(@" @@ -87,13 +85,12 @@ Compilation createCompilation(string program) [TestMethod] public void AzResourceTypeProvider_should_error_for_top_level_and_warn_for_nested_properties() { - var typeLoader = CreateMockTypeLoader(ResourceTypeReference.Parse("Mock.Rp/mockType@2020-01-01")); Compilation createCompilation(string program) - => new Compilation(new AzResourceTypeProvider(typeLoader), SyntaxTreeGroupingFactory.CreateFromText(program)); + => new Compilation(BuiltInTestTypes.Create(), SyntaxTreeGroupingFactory.CreateFromText(program)); // Missing top-level properties - should be an error var compilation = createCompilation(@" -resource missingRequired 'Mock.Rp/mockType@2020-01-01' = { +resource missingRequired 'Test.Rp/readWriteTests@2020-01-01' = { name: 'missingRequired' } "); @@ -103,7 +100,7 @@ Compilation createCompilation(string program) // Top-level properties that aren't part of the type definition - should be an error compilation = createCompilation(@" -resource unexpectedTopLevel 'Mock.Rp/mockType@2020-01-01' = { +resource unexpectedTopLevel 'Test.Rp/readWriteTests@2020-01-01' = { name: 'unexpectedTopLevel' properties: { required: 'hello!' @@ -112,12 +109,12 @@ Compilation createCompilation(string program) } "); compilation.Should().HaveDiagnostics(new [] { - ("BCP038", DiagnosticLevel.Error, "The property \"madeUpProperty\" is not allowed on objects of type \"Mock.Rp/mockType@2020-01-01\". Permissible properties include \"dependsOn\"."), + ("BCP038", DiagnosticLevel.Error, "The property \"madeUpProperty\" is not allowed on objects of type \"Test.Rp/readWriteTests@2020-01-01\". Permissible properties include \"dependsOn\"."), }); // Missing non top-level properties - should be a warning compilation = createCompilation(@" -resource missingRequiredProperty 'Mock.Rp/mockType@2020-01-01' = { +resource missingRequiredProperty 'Test.Rp/readWriteTests@2020-01-01' = { name: 'missingRequiredProperty' properties: { } @@ -129,7 +126,7 @@ Compilation createCompilation(string program) // Non top-level properties that aren't part of the type definition - should be a warning compilation = createCompilation(@" -resource unexpectedPropertiesProperty 'Mock.Rp/mockType@2020-01-01' = { +resource unexpectedPropertiesProperty 'Test.Rp/readWriteTests@2020-01-01' = { name: 'unexpectedPropertiesProperty' properties: { required: 'hello!' @@ -197,59 +194,5 @@ private static void VisitAllReachableTypes(TypeSymbol typeSymbol, HashSet().Single(); - - var mockTypeLocation = new TypeLocation(); - var mockTypeLoader = new Mock(); - var resourceTypes = new Dictionary - { - [resourceTypeReference.FormatName()] = mockTypeLocation, - }; - mockTypeLoader.Setup(x => x.GetIndexedTypes()).Returns(new Azure.Bicep.Types.Az.Index.TypeIndex(resourceTypes)); - mockTypeLoader.Setup(x => x.LoadResourceType(mockTypeLocation)).Returns(resourceType); - - return mockTypeLoader.Object; - } - - private static string CreateSerializedTypes(ResourceTypeReference resourceTypeReference) - { - var typeFactory = new Azure.Bicep.Types.Concrete.TypeFactory(Enumerable.Empty()); - var stringType = typeFactory.Create(() => new Azure.Bicep.Types.Concrete.BuiltInType(Azure.Bicep.Types.Concrete.BuiltInTypeKind.String)); - var apiVersionType = typeFactory.Create(() => new Azure.Bicep.Types.Concrete.StringLiteralType(resourceTypeReference.ApiVersion)); - var typeType = typeFactory.Create(() => new Azure.Bicep.Types.Concrete.StringLiteralType(resourceTypeReference.FullyQualifiedType)); - var propertiesType = typeFactory.Create(() => new Azure.Bicep.Types.Concrete.ObjectType( - "Properties", - new Dictionary - { - ["readwrite"] = new Azure.Bicep.Types.Concrete.ObjectProperty(typeFactory.GetReference(stringType), Azure.Bicep.Types.Concrete.ObjectPropertyFlags.None, "readwrite property"), - ["readonly"] = new Azure.Bicep.Types.Concrete.ObjectProperty(typeFactory.GetReference(stringType), Azure.Bicep.Types.Concrete.ObjectPropertyFlags.ReadOnly, "readonly property"), - ["writeonly"] = new Azure.Bicep.Types.Concrete.ObjectProperty(typeFactory.GetReference(stringType), Azure.Bicep.Types.Concrete.ObjectPropertyFlags.WriteOnly, "writeonly property"), - ["required"] = new Azure.Bicep.Types.Concrete.ObjectProperty(typeFactory.GetReference(stringType), Azure.Bicep.Types.Concrete.ObjectPropertyFlags.Required, "required property"), - }, - null)); - var bodyType = typeFactory.Create(() => new Azure.Bicep.Types.Concrete.ObjectType( - resourceTypeReference.FormatName(), - new Dictionary - { - ["name"] = new Azure.Bicep.Types.Concrete.ObjectProperty(typeFactory.GetReference(stringType), Azure.Bicep.Types.Concrete.ObjectPropertyFlags.DeployTimeConstant | Azure.Bicep.Types.Concrete.ObjectPropertyFlags.Required, "name property"), - ["type"] = new Azure.Bicep.Types.Concrete.ObjectProperty(typeFactory.GetReference(typeType), Azure.Bicep.Types.Concrete.ObjectPropertyFlags.DeployTimeConstant | Azure.Bicep.Types.Concrete.ObjectPropertyFlags.ReadOnly, "type property"), - ["apiVersion"] = new Azure.Bicep.Types.Concrete.ObjectProperty(typeFactory.GetReference(apiVersionType), Azure.Bicep.Types.Concrete.ObjectPropertyFlags.DeployTimeConstant | Azure.Bicep.Types.Concrete.ObjectPropertyFlags.ReadOnly, "apiVersion property"), - ["id"] = new Azure.Bicep.Types.Concrete.ObjectProperty(typeFactory.GetReference(stringType), Azure.Bicep.Types.Concrete.ObjectPropertyFlags.DeployTimeConstant | Azure.Bicep.Types.Concrete.ObjectPropertyFlags.ReadOnly, "id property"), - ["properties"] = new Azure.Bicep.Types.Concrete.ObjectProperty(typeFactory.GetReference(propertiesType), Azure.Bicep.Types.Concrete.ObjectPropertyFlags.Required, "properties property"), - }, - null)); - - typeFactory.Create(() => new Azure.Bicep.Types.Concrete.ResourceType( - resourceTypeReference.FormatName(), - Azure.Bicep.Types.Concrete.ScopeType.ResourceGroup, - typeFactory.GetReference(bodyType))); - - return TypeSerializer.Serialize(typeFactory.GetTypes()); - } } } \ No newline at end of file diff --git a/src/Bicep.Core.UnitTests/Utils/BuiltInTestTypes.cs b/src/Bicep.Core.UnitTests/Utils/BuiltInTestTypes.cs new file mode 100644 index 00000000000..86a9efefd89 --- /dev/null +++ b/src/Bicep.Core.UnitTests/Utils/BuiltInTestTypes.cs @@ -0,0 +1,157 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using System.Collections.Generic; +using Azure.Bicep.Types.Concrete; +using Bicep.Core.Resources; + +namespace Bicep.Core.UnitTests.Utils +{ + /// + /// A set of reusable resource types to validate various pieces of functionality. + /// + public class BuiltInTestTypes + { + private readonly TypeFactory factory; + private readonly IReadOnlyDictionary builtInTypes; + + private BuiltInTestTypes(TypeFactory factory) + { + this.factory = factory; + this.builtInTypes = new Dictionary + { + [BuiltInTypeKind.Any] = AddType(factory, new BuiltInType(BuiltInTypeKind.Any)), + [BuiltInTypeKind.Null] = AddType(factory, new BuiltInType(BuiltInTypeKind.Null)), + [BuiltInTypeKind.Bool] = AddType(factory, new BuiltInType(BuiltInTypeKind.Bool)), + [BuiltInTypeKind.Int] = AddType(factory, new BuiltInType(BuiltInTypeKind.Int)), + [BuiltInTypeKind.String] = AddType(factory, new BuiltInType(BuiltInTypeKind.String)), + [BuiltInTypeKind.Object] = AddType(factory, new BuiltInType(BuiltInTypeKind.Object)), + [BuiltInTypeKind.Array] = AddType(factory, new BuiltInType(BuiltInTypeKind.Array)), + }; + + RegisterBasicTestsType(); + RegisterReadWriteTestsType(); + RegisterDiscriminatorTestsType(); + } + + private IReadOnlyDictionary GetDefaultResourceProperties(ResourceTypeReference resourceType) + { + var apiVersionType = AddType(factory, new StringLiteralType(resourceType.ApiVersion)); + var typeType = AddType(factory, new StringLiteralType(resourceType.FullyQualifiedType)); + var stringType = builtInTypes[BuiltInTypeKind.String]; + + return new Dictionary + { + ["name"] = new(stringType, ObjectPropertyFlags.DeployTimeConstant | ObjectPropertyFlags.Required, "name property"), + ["type"] = new(typeType, ObjectPropertyFlags.DeployTimeConstant | ObjectPropertyFlags.ReadOnly, "type property"), + ["apiVersion"] = new(apiVersionType, ObjectPropertyFlags.DeployTimeConstant | ObjectPropertyFlags.ReadOnly, "apiVersion property"), + ["id"] = new(stringType, ObjectPropertyFlags.DeployTimeConstant | ObjectPropertyFlags.ReadOnly, "id property"), + }; + } + + private void RegisterBasicTestsType() + { + var resourceType = ResourceTypeReference.Parse("Test.Rp/basicTests@2020-01-01"); + + var stringType = builtInTypes[BuiltInTypeKind.String]; + + var bodyType = AddType(factory, new ObjectType( + resourceType.FormatName(), + new Dictionary(GetDefaultResourceProperties(resourceType)) + { + ["kind"] = new(stringType, ObjectPropertyFlags.ReadOnly, "kind property"), + }, + null)); + + AddType(factory, new ResourceType(resourceType.FormatName(), ScopeType.ResourceGroup, bodyType)); + } + + private void RegisterReadWriteTestsType() + { + var resourceType = ResourceTypeReference.Parse("Test.Rp/readWriteTests@2020-01-01"); + + var stringType = builtInTypes[BuiltInTypeKind.String]; + + var propertiesType = AddType(factory, new ObjectType( + "Properties", + new Dictionary + { + ["readwrite"] = new(stringType, ObjectPropertyFlags.None, "This is a property which supports reading AND writing!"), + ["readonly"] = new(stringType, ObjectPropertyFlags.ReadOnly, "This is a property which only supports reading."), + ["writeonly"] = new(stringType, ObjectPropertyFlags.WriteOnly, "This is a property which only supports writing."), + ["required"] = new(stringType, ObjectPropertyFlags.Required, "This is a property which is required."), + }, + null)); + + var bodyType = AddType(factory, new ObjectType( + resourceType.FormatName(), + new Dictionary(GetDefaultResourceProperties(resourceType)) + { + ["properties"] = new(propertiesType, ObjectPropertyFlags.Required, "properties property"), + }, + null)); + + AddType(factory, new ResourceType(resourceType.FormatName(), ScopeType.ResourceGroup, bodyType)); + } + + private void RegisterDiscriminatorTestsType() + { + var resourceType = ResourceTypeReference.Parse("Test.Rp/discriminatorTests@2020-01-01"); + + var stringType = builtInTypes[BuiltInTypeKind.String]; + + var bodyAProps = AddType(factory, new ObjectType( + "BodyAProperties", + new Dictionary + { + ["propA"] = new(stringType, ObjectPropertyFlags.None, "This is the description for propA!"), + }, + null)); + + var bodyBProps = AddType(factory, new ObjectType( + "BodyBProperties", + new Dictionary + { + ["propB"] = new(stringType, ObjectPropertyFlags.None, "This is the description for propB!"), + }, + null)); + + var bodyType = AddType(factory, new DiscriminatedObjectType( + resourceType.FormatName(), + "kind", + new Dictionary(GetDefaultResourceProperties(resourceType)), + new Dictionary + { + ["BodyA"] = AddType(factory, new ObjectType( + "BodyA", + new Dictionary + { + ["kind"] = new(AddType(factory, new StringLiteralType("BodyA")), ObjectPropertyFlags.None, "This is the kind of body A"), + ["properties"] = new(bodyAProps, ObjectPropertyFlags.None, "These are the properties for body A"), + }, + null)), + ["BodyB"] = AddType(factory, new ObjectType( + "BodyB", + new Dictionary + { + ["kind"] = new(AddType(factory, new StringLiteralType("BodyB")), ObjectPropertyFlags.None, "This is the kind of body B"), + ["properties"] = new(bodyBProps, ObjectPropertyFlags.None, "These are the properties for body B"), + }, + null)), + })); + + AddType(factory, new ResourceType(resourceType.FormatName(), ScopeType.ResourceGroup, bodyType)); + } + + private static ITypeReference AddType(TypeFactory factory, TypeBase type) + { + type = factory.Create(() => type); + + return factory.GetReference(type); + } + + public static Core.TypeSystem.IResourceTypeProvider Create() + => ResourceTypeProviderHelper.CreateAzResourceTypeProvider(factory => { + new BuiltInTestTypes(factory); + }); + } +} \ No newline at end of file diff --git a/src/Bicep.Core.UnitTests/Utils/ParserHelper.cs b/src/Bicep.Core.UnitTests/Utils/ParserHelper.cs index 5c5a86bbe0e..fad34855933 100644 --- a/src/Bicep.Core.UnitTests/Utils/ParserHelper.cs +++ b/src/Bicep.Core.UnitTests/Utils/ParserHelper.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; +using System.Collections.Generic; using Bicep.Core.Parsing; using Bicep.Core.Syntax; @@ -16,6 +18,22 @@ public static ProgramSyntax Parse(string text) } public static SyntaxBase ParseExpression(string text, ExpressionFlags expressionFlags = ExpressionFlags.AllowComplexLiterals) => new Parser(text).Expression(expressionFlags); + + public static (string file, IReadOnlyList cursors) GetFileWithCursors(string fileWithCursors) + { + var bicepFile = fileWithCursors.Replace("|", ""); + + var cursors = new List(); + for (var i = 0; i < fileWithCursors.Length; i++) + { + if (fileWithCursors[i] == '|') + { + cursors.Add(i - cursors.Count); + } + } + + return (bicepFile, cursors); + } } } diff --git a/src/Bicep.Core.UnitTests/Utils/PrintHelper.cs b/src/Bicep.Core.UnitTests/Utils/PrintHelper.cs index 45b3d929e87..4d2a6b9dd69 100644 --- a/src/Bicep.Core.UnitTests/Utils/PrintHelper.cs +++ b/src/Bicep.Core.UnitTests/Utils/PrintHelper.cs @@ -64,6 +64,11 @@ private static string[] GetProgramTextLines(SyntaxTree syntaxTree) public static string PrintWithAnnotations(SyntaxTree syntaxTree, IEnumerable annotations, int context, bool includeLineNumbers) { + if (!annotations.Any()) + { + return ""; + } + var output = new StringBuilder(); var programLines = GetProgramTextLines(syntaxTree); diff --git a/src/Bicep.Core.UnitTests/Utils/TestSyntaxHelper.cs b/src/Bicep.Core.UnitTests/Utils/TestSyntaxHelper.cs index 84f20330e0c..a1e3572737a 100644 --- a/src/Bicep.Core.UnitTests/Utils/TestSyntaxHelper.cs +++ b/src/Bicep.Core.UnitTests/Utils/TestSyntaxHelper.cs @@ -3,27 +3,14 @@ using Bicep.Core.Navigation; using Bicep.Core.Syntax; -using Bicep.Core.Semantics; namespace Bicep.Core.UnitTests.Utils { public static class TestSyntaxHelper { - public static bool NodeShouldBeBound(ISymbolReference symbolReference, SemanticModel semanticModel) - { - if (!(symbolReference is InstanceFunctionCallSyntax instanceFunctionCallSyntax)) - { - return true; - } - - if (instanceFunctionCallSyntax.BaseExpression is VariableAccessSyntax baseVariableSyntax && - semanticModel.Root.ImportedNamespaces.ContainsKey(baseVariableSyntax.Name.IdentifierName)) - { - // we only expect to have bound InstanceFunctionCallsSyntax if they accessed on a namespace - e.g. sys.concat(..) - return true; - } - - return false; - } + public static bool NodeShouldBeBound(ISymbolReference symbolReference) + => symbolReference is not InstanceFunctionCallSyntax + and not PropertyAccessSyntax + and not ObjectPropertySyntax; } -} +} \ No newline at end of file diff --git a/src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs b/src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs index 05c2da9a483..5bda2e6fda8 100644 --- a/src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs +++ b/src/Bicep.Core/Diagnostics/DiagnosticBuilder.cs @@ -619,15 +619,15 @@ public ErrorDiagnostic ArgumentCountMismatch(int argumentCount, int mininumArgum "BCP106", "Expected a new line character at this location. Commas are not used as separator delimiters."); - public ErrorDiagnostic FunctionDoesNotExistInNamespace(Symbol namespaceSymbol, string name) => new( + public ErrorDiagnostic FunctionDoesNotExistInNamespace(Symbol namespaceType, string name) => new( TextSpan, "BCP107", - $"The function \"{name}\" does not exist in namespace \"{namespaceSymbol.Name}\"."); + $"The function \"{name}\" does not exist in namespace \"{namespaceType.Name}\"."); - public FixableErrorDiagnostic FunctionDoesNotExistInNamespaceWithSuggestion(Symbol namespaceSymbol, string name, string suggestedName) => new( + public FixableErrorDiagnostic FunctionDoesNotExistInNamespaceWithSuggestion(Symbol namespaceType, string name, string suggestedName) => new( TextSpan, "BCP108", - $"The function \"{name}\" does not exist in namespace \"{namespaceSymbol.Name}\". Did you mean \"{suggestedName}\"?", + $"The function \"{name}\" does not exist in namespace \"{namespaceType.Name}\". Did you mean \"{suggestedName}\"?", null, new CodeFix($"Change \"{name}\" to \"{suggestedName}\"", true, CodeManipulator.Replace(TextSpan, suggestedName))); diff --git a/src/Bicep.Core/Extensions/FunctionFlagsExtensions.cs b/src/Bicep.Core/Extensions/FunctionFlagsExtensions.cs index 65ab60ae0da..1400241230b 100644 --- a/src/Bicep.Core/Extensions/FunctionFlagsExtensions.cs +++ b/src/Bicep.Core/Extensions/FunctionFlagsExtensions.cs @@ -6,15 +6,8 @@ namespace Bicep.Core.Extensions { public static class FunctionFlagsExtensions { - private const FunctionFlags DecoratorFlags = - FunctionFlags.ParameterDecorator | - FunctionFlags.VariableDecorator | - FunctionFlags.ResourceDecorator | - FunctionFlags.ModuleDecorator | - FunctionFlags.OutputDecorator; + public static bool HasAnyDecoratorFlag(this FunctionFlags functionFlags) => (functionFlags & FunctionFlags.AnyDecorator) != 0; - public static bool HasAnyDecoratorFlag(this FunctionFlags functionFlags) => (functionFlags & DecoratorFlags) != 0; - - public static bool HasAllDecoratorFlags(this FunctionFlags functionFlags) => (functionFlags & DecoratorFlags) == DecoratorFlags; + public static bool HasAllDecoratorFlags(this FunctionFlags functionFlags) => (functionFlags & FunctionFlags.AnyDecorator) == FunctionFlags.AnyDecorator; } } diff --git a/src/Bicep.Core/Semantics/Binder.cs b/src/Bicep.Core/Semantics/Binder.cs index 211163823ab..df5e8e89d07 100644 --- a/src/Bicep.Core/Semantics/Binder.cs +++ b/src/Bicep.Core/Semantics/Binder.cs @@ -55,15 +55,6 @@ public Binder(SyntaxTree syntaxTree, ISymbolContext symbolContext) /// the syntax node public Symbol? GetSymbolInfo(SyntaxBase syntax) => this.bindings.TryGetValue(syntax); - /// - /// Returns all syntax nodes that represent a reference to the specified symbol. This includes the definitions of the symbol as well. - /// Unusued declarations will return 1 result. Unused and undeclared symbols (functions, namespaces, for example) may return an empty list. - /// - /// The symbol - public IEnumerable FindReferences(Symbol symbol) => this.bindings - .Where(binding => ReferenceEquals(binding.Value, symbol)) - .Select(binding => binding.Key); - public ImmutableArray? TryGetCycle(DeclaredSymbol declaredSymbol) => this.cyclesBySymbol.TryGetValue(declaredSymbol, out var cycle) ? cycle : null; diff --git a/src/Bicep.Core/Semantics/IBinder.cs b/src/Bicep.Core/Semantics/IBinder.cs index 0d2b969aece..12db93a32de 100644 --- a/src/Bicep.Core/Semantics/IBinder.cs +++ b/src/Bicep.Core/Semantics/IBinder.cs @@ -12,8 +12,6 @@ public interface IBinder : ISyntaxHierarchy ResourceScope TargetScope { get; } FileSymbol FileSymbol { get; } - - IEnumerable FindReferences(Symbol symbol); Symbol? GetSymbolInfo(SyntaxBase syntax); diff --git a/src/Bicep.Core/Semantics/NameBindingVisitor.cs b/src/Bicep.Core/Semantics/NameBindingVisitor.cs index f7552d45396..4d4e582db75 100644 --- a/src/Bicep.Core/Semantics/NameBindingVisitor.cs +++ b/src/Bicep.Core/Semantics/NameBindingVisitor.cs @@ -224,41 +224,6 @@ public override void VisitFunctionCallSyntax(FunctionCallSyntax syntax) this.bindings.Add(syntax, symbol); } - public override void VisitInstanceFunctionCallSyntax(InstanceFunctionCallSyntax syntax) - { - FunctionFlags currentFlags = allowedFlags; - this.Visit(syntax.BaseExpression); - this.Visit(syntax.Dot); - this.Visit(syntax.Name); - this.Visit(syntax.OpenParen); - allowedFlags = allowedFlags.HasAnyDecoratorFlag() ? FunctionFlags.Default : allowedFlags; - this.VisitNodes(syntax.Arguments); - this.Visit(syntax.CloseParen); - allowedFlags = currentFlags; - - if (!syntax.Name.IsValid) - { - // the parser produced an instance function calls with an invalid name - // all instance function calls must be bound to a symbol, so let's - // bind to a symbol without any errors (there's already a parse error) - this.bindings.Add(syntax, new ErrorSymbol()); - return; - } - - if (bindings.TryGetValue(syntax.BaseExpression, out var baseSymbol) && baseSymbol is NamespaceSymbol namespaceSymbol) - { - var functionSymbol = allowedFlags.HasAnyDecoratorFlag() - // Decorator functions are only valid when HasDecoratorFlag() is true which means - // the instance function call is the top level expression of a DecoratorSyntax node. - ? namespaceSymbol.Type.MethodResolver.TryGetSymbol(syntax.Name) ?? namespaceSymbol.Type.DecoratorResolver.TryGetSymbol(syntax.Name) - : namespaceSymbol.Type.MethodResolver.TryGetSymbol(syntax.Name); - - var foundSymbol = SymbolValidator.ResolveNamespaceQualifiedFunction(allowedFlags, functionSymbol, syntax.Name, namespaceSymbol); - - this.bindings.Add(syntax, foundSymbol); - } - } - protected override void VisitInternal(SyntaxBase syntax) { // any node can be a binding scope diff --git a/src/Bicep.Core/Semantics/PropertySymbol.cs b/src/Bicep.Core/Semantics/PropertySymbol.cs new file mode 100644 index 00000000000..5a69df7a089 --- /dev/null +++ b/src/Bicep.Core/Semantics/PropertySymbol.cs @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Bicep.Core.TypeSystem; + +namespace Bicep.Core.Semantics +{ + public class PropertySymbol : Symbol + { + public PropertySymbol(string name, string? description, TypeSymbol type) + : base(name) + { + Description = description; + Type = type; + } + + public override SymbolKind Kind => SymbolKind.Property; + + public string? Description { get; } + + public TypeSymbol Type { get; } + + public override void Accept(SymbolVisitor visitor) => visitor.VisitPropertySymbol(this); + } +} diff --git a/src/Bicep.Core/Semantics/SemanticModel.cs b/src/Bicep.Core/Semantics/SemanticModel.cs index babcc4daddc..7104f63436d 100644 --- a/src/Bicep.Core/Semantics/SemanticModel.cs +++ b/src/Bicep.Core/Semantics/SemanticModel.cs @@ -7,6 +7,7 @@ using Bicep.Core.Emit; using Bicep.Core.Extensions; using Bicep.Core.Syntax; +using Bicep.Core.Syntax.Visitors; using Bicep.Core.TypeSystem; namespace Bicep.Core.Semantics @@ -114,14 +115,87 @@ public bool HasErrors() /// a symbol will always be returned. Binding failures are represented with a non-null error symbol. /// /// the syntax node - public Symbol? GetSymbolInfo(SyntaxBase syntax) => this.Binder.GetSymbolInfo(syntax); + public Symbol? GetSymbolInfo(SyntaxBase syntax) + { + static PropertySymbol? GetPropertySymbol(TypeSymbol? baseType, string property) + { + if (baseType is null) + { + return null; + } + + var typeProperty = TypeAssignmentVisitor.UnwrapType(baseType) switch { + ObjectType x => x.Properties.TryGetValue(property, out var tp) ? tp : null, + DiscriminatedObjectType x => x.TryGetDiscriminatorProperty(property), + _ => null + }; + + if (typeProperty is null) + { + return null; + } + + return new PropertySymbol(property, typeProperty.Description, typeProperty.TypeReference.Type); + } + + switch (syntax) + { + case InstanceFunctionCallSyntax ifc: + { + var baseType = GetTypeInfo(ifc.BaseExpression); + switch (baseType) + { + case NamespaceType namespaceType when SyntaxTree.Hierarchy.GetParent(ifc) is DecoratorSyntax: + return namespaceType.DecoratorResolver.TryGetSymbol(ifc.Name); + case ObjectType objectType: + return objectType.MethodResolver.TryGetSymbol(ifc.Name); + } + + return null; + } + case PropertyAccessSyntax propertyAccess: + { + var baseType = GetDeclaredType(propertyAccess.BaseExpression); + var property = propertyAccess.PropertyName.IdentifierName; + + return GetPropertySymbol(baseType, property); + } + case ObjectPropertySyntax objectProperty: + { + if (Binder.GetParent(objectProperty) is not {} parentSyntax) + { + return null; + } + + var baseType = GetDeclaredType(parentSyntax); + if (objectProperty.TryGetKeyText() is not {} property) + { + return null; + } + + return GetPropertySymbol(baseType, property); + } + } + + return this.Binder.GetSymbolInfo(syntax); + } /// /// Returns all syntax nodes that represent a reference to the specified symbol. This includes the definitions of the symbol as well. /// Unusued declarations will return 1 result. Unused and undeclared symbols (functions, namespaces, for example) may return an empty list. /// /// The symbol - public IEnumerable FindReferences(Symbol symbol) => this.Binder.FindReferences(symbol); + public IEnumerable FindReferences(Symbol symbol) + => SyntaxAggregator.Aggregate(this.SyntaxTree.ProgramSyntax, new List(), (accumulated, current) => + { + if (object.ReferenceEquals(symbol, this.GetSymbolInfo(current))) + { + accumulated.Add(current); + } + + return accumulated; + }, + accumulated => accumulated); /// /// Gets the file that was compiled. diff --git a/src/Bicep.Core/Semantics/SymbolKind.cs b/src/Bicep.Core/Semantics/SymbolKind.cs index 61c26673a6e..4d23160bfe8 100644 --- a/src/Bicep.Core/Semantics/SymbolKind.cs +++ b/src/Bicep.Core/Semantics/SymbolKind.cs @@ -15,6 +15,7 @@ public enum SymbolKind Namespace, Function, Local, - Scope + Scope, + Property } } diff --git a/src/Bicep.Core/Semantics/SymbolValidator.cs b/src/Bicep.Core/Semantics/SymbolValidator.cs index 5651f6ba8e2..149bbad7b1c 100644 --- a/src/Bicep.Core/Semantics/SymbolValidator.cs +++ b/src/Bicep.Core/Semantics/SymbolValidator.cs @@ -18,26 +18,32 @@ public static class SymbolValidator private delegate IEnumerable GetNameSuggestions(); private delegate ErrorDiagnostic GetMissingNameError(DiagnosticBuilder.DiagnosticBuilderInternal builder, string? suggestedName); - public static Symbol ResolveNamespaceQualifiedFunction(FunctionFlags allowedFlags, Symbol? foundSymbol, IdentifierSyntax identifierSyntax, NamespaceSymbol namespaceSymbol) + public static Symbol ResolveNamespaceQualifiedFunction(FunctionFlags allowedFlags, Symbol? foundSymbol, IdentifierSyntax identifierSyntax, NamespaceType namespaceType) => ResolveSymbolInternal( allowedFlags, foundSymbol, identifierSyntax, getNameSuggestions: () => { - var knowFunctionNames = namespaceSymbol.Type.MethodResolver.GetKnownFunctions().Keys; + var knowFunctionNames = namespaceType.MethodResolver.GetKnownFunctions().Keys; return allowedFlags.HasAnyDecoratorFlag() - ? knowFunctionNames.Concat(namespaceSymbol.Type.DecoratorResolver.GetKnownDecoratorFunctions().Keys) + ? knowFunctionNames.Concat(namespaceType.DecoratorResolver.GetKnownDecoratorFunctions().Keys) : knowFunctionNames; }, getMissingNameError: (builder, suggestedName) => suggestedName switch { - null => builder.FunctionDoesNotExistInNamespace(namespaceSymbol, identifierSyntax.IdentifierName), - _ => builder.FunctionDoesNotExistInNamespaceWithSuggestion(namespaceSymbol, identifierSyntax.IdentifierName, suggestedName), + null => builder.FunctionDoesNotExistInNamespace(namespaceType, identifierSyntax.IdentifierName), + _ => builder.FunctionDoesNotExistInNamespaceWithSuggestion(namespaceType, identifierSyntax.IdentifierName, suggestedName), }); public static Symbol ResolveObjectQualifiedFunction(Symbol? foundSymbol, IdentifierSyntax identifierSyntax, ObjectType objectType) - => ResolveSymbolInternal( + { + if (objectType is NamespaceType namespaceType) + { + return ResolveNamespaceQualifiedFunction(FunctionFlags.Default, foundSymbol, identifierSyntax, namespaceType); + } + + return ResolveSymbolInternal( FunctionFlags.Default, foundSymbol, identifierSyntax, @@ -46,6 +52,7 @@ public static Symbol ResolveObjectQualifiedFunction(Symbol? foundSymbol, Identif null => builder.FunctionDoesNotExistOnObject(objectType, identifierSyntax.IdentifierName), _ => builder.FunctionDoesNotExistOnObjectWithSuggestion(objectType, identifierSyntax.IdentifierName, suggestedName), }); + } public static Symbol ResolveUnqualifiedFunction(FunctionFlags allowedFlags, Symbol? foundSymbol, IdentifierSyntax identifierSyntax, IEnumerable namespaces) => ResolveSymbolInternal( diff --git a/src/Bicep.Core/Semantics/SymbolVisitor.cs b/src/Bicep.Core/Semantics/SymbolVisitor.cs index a55a44091e3..a0a58adeeb4 100644 --- a/src/Bicep.Core/Semantics/SymbolVisitor.cs +++ b/src/Bicep.Core/Semantics/SymbolVisitor.cs @@ -61,6 +61,11 @@ public virtual void VisitFunctionSymbol(FunctionSymbol symbol) VisitDescendants(symbol); } + public virtual void VisitPropertySymbol(PropertySymbol symbol) + { + VisitDescendants(symbol); + } + public virtual void VisitLocalScope(LocalScope symbol) { VisitDescendants(symbol); diff --git a/src/Bicep.Core/Syntax/SyntaxVisitor.cs b/src/Bicep.Core/Syntax/SyntaxVisitor.cs index fc5c85c750f..d05411caba4 100644 --- a/src/Bicep.Core/Syntax/SyntaxVisitor.cs +++ b/src/Bicep.Core/Syntax/SyntaxVisitor.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System.Collections.Generic; using System.Linq; +using System.Runtime.CompilerServices; using Bicep.Core.Parsing; namespace Bicep.Core.Syntax @@ -15,6 +16,8 @@ public void Visit(SyntaxBase? node) return; } + RuntimeHelpers.EnsureSufficientExecutionStack(); + VisitInternal(node); } diff --git a/src/Bicep.Core/TypeSystem/Az/AzResourceTypeFactory.cs b/src/Bicep.Core/TypeSystem/Az/AzResourceTypeFactory.cs index 7cb739a4e46..195beb93584 100644 --- a/src/Bicep.Core/TypeSystem/Az/AzResourceTypeFactory.cs +++ b/src/Bicep.Core/TypeSystem/Az/AzResourceTypeFactory.cs @@ -39,7 +39,7 @@ private ITypeReference GetTypeReference(Azure.Bicep.Types.Concrete.ITypeReferenc private TypeProperty GetTypeProperty(string name, Azure.Bicep.Types.Concrete.ObjectProperty input) { - return new TypeProperty(name, GetTypeReference(input.Type), GetTypePropertyFlags(input)); + return new TypeProperty(name, GetTypeReference(input.Type), GetTypePropertyFlags(input), input.Description); } private static TypePropertyFlags GetTypePropertyFlags(Azure.Bicep.Types.Concrete.ObjectProperty input) diff --git a/src/Bicep.Core/TypeSystem/Az/AzResourceTypeProvider.cs b/src/Bicep.Core/TypeSystem/Az/AzResourceTypeProvider.cs index 3f7f678a172..79233e8afe7 100644 --- a/src/Bicep.Core/TypeSystem/Az/AzResourceTypeProvider.cs +++ b/src/Bicep.Core/TypeSystem/Az/AzResourceTypeProvider.cs @@ -132,7 +132,7 @@ public static ResourceType SetBicepResourceProperties(ResourceType resourceType, bodyType = SetBicepResourceProperties(bodyObjectType, resourceType.ValidParentScopes, resourceType.TypeReference, flags); break; case DiscriminatedObjectType bodyDiscriminatedType: - if (LanguageConstants.IdentifierComparer.Equals(bodyDiscriminatedType.DiscriminatorKey, LanguageConstants.ResourceNamePropertyName) && + if (bodyDiscriminatedType.TryGetDiscriminatorProperty(LanguageConstants.ResourceNamePropertyName) is not null && !flags.HasFlag(ResourceTypeGenerationFlags.PermitLiteralNameProperty)) { // The 'name' property doesn't support fixed value names (e.g. we're in a top-level child resource declaration). diff --git a/src/Bicep.Core/TypeSystem/DeclaredTypeManager.cs b/src/Bicep.Core/TypeSystem/DeclaredTypeManager.cs index bf1b5b9b253..0a7c8c80f85 100644 --- a/src/Bicep.Core/TypeSystem/DeclaredTypeManager.cs +++ b/src/Bicep.Core/TypeSystem/DeclaredTypeManager.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Runtime.CompilerServices; using Bicep.Core.Diagnostics; using Bicep.Core.Extensions; using Bicep.Core.Parsing; @@ -53,6 +54,8 @@ public DeclaredTypeManager(IResourceTypeProvider resourceTypeProvider, TypeManag private DeclaredTypeAssignment? GetTypeAssignment(SyntaxBase syntax) { + RuntimeHelpers.EnsureSufficientExecutionStack(); + switch (syntax) { case ParameterDeclarationSyntax parameter: @@ -89,7 +92,7 @@ public DeclaredTypeManager(IResourceTypeProvider resourceTypeProvider, TypeManag return GetArrayAccessType(arrayAccess); case VariableDeclarationSyntax variable: - return new DeclaredTypeAssignment(this.typeManager.GetTypeInfo(variable), variable); + return new DeclaredTypeAssignment(this.typeManager.GetTypeInfo(variable.Value), variable); case LocalVariableSyntax localVariable: return new DeclaredTypeAssignment(this.typeManager.GetTypeInfo(localVariable), localVariable); @@ -196,6 +199,7 @@ private DeclaredTypeAssignment GetModuleType(ModuleDeclarationSyntax syntax) var body = baseExpressionAssignment?.DeclaringSyntax switch { ResourceDeclarationSyntax resourceDeclarationSyntax => resourceDeclarationSyntax.TryGetBody(), + ModuleDeclarationSyntax moduleDeclarationSyntax => moduleDeclarationSyntax.TryGetBody(), _ => baseExpressionAssignment?.DeclaringSyntax as ObjectSyntax, }; return GetObjectPropertyType( @@ -383,8 +387,14 @@ private DeclaredTypeAssignment GetModuleType(ModuleDeclarationSyntax syntax) return null; } - var parentTypeAssignment = GetDeclaredTypeAssignment(parent); - if (parentTypeAssignment == null) + var parentTypeAssignment = parent switch { + // variable declared type is calculated using its assigned type, so querying it here causes endless recursion. + // we can shortcut that by returning any[] here + VariableDeclarationSyntax var => new DeclaredTypeAssignment(LanguageConstants.Array, var), + _ => GetDeclaredTypeAssignment(parent), + }; + + if (parentTypeAssignment is null) { return null; } @@ -429,61 +439,91 @@ private DeclaredTypeAssignment GetModuleType(ModuleDeclarationSyntax syntax) private DeclaredTypeAssignment? GetObjectType(ObjectSyntax syntax) { var parent = this.binder.GetParent(syntax); - - if (parent == null) - { - return null; - } - - var parentTypeAssignment = GetDeclaredTypeAssignment(parent); - if (parentTypeAssignment == null) + if (parent is null) { return null; } - var parentType = parentTypeAssignment.Reference.Type; - switch (parent) { - case ResourceDeclarationSyntax when parentType is ResourceType resourceType: + case ResourceDeclarationSyntax: + if (GetDeclaredTypeAssignment(parent)?.Reference.Type is not ResourceType resourceType) + { + return null; + } + // the object literal's parent is a resource declaration, which makes this the body of the resource // the declared type will be the same as the parent return TryCreateAssignment(ResolveDiscriminatedObjects(resourceType.Body.Type, syntax), syntax); - case ModuleDeclarationSyntax when parentType is ModuleType moduleType: + case ModuleDeclarationSyntax: + if (GetDeclaredTypeAssignment(parent)?.Reference.Type is not ModuleType moduleType) + { + return null; + } + // the object literal's parent is a module declaration, which makes this the body of the module // the declared type will be the same as the parent return TryCreateAssignment(ResolveDiscriminatedObjects(moduleType.Body.Type, syntax), syntax); case IfConditionSyntax: + if (GetDeclaredTypeAssignment(parent) is not {} ifParentTypeAssignment) + { + return null; + } + // if-condition declared type already resolved discriminators and used the object as the declaring syntax - Debug.Assert(ReferenceEquals(syntax, parentTypeAssignment.DeclaringSyntax), "ReferenceEquals(syntax,parentTypeAssignment.DeclaringSyntax)"); + Debug.Assert(ReferenceEquals(syntax, ifParentTypeAssignment.DeclaringSyntax), "ReferenceEquals(syntax,parentTypeAssignment.DeclaringSyntax)"); // the declared type will be the same as the parent - return parentTypeAssignment; + return ifParentTypeAssignment; + + case ForSyntax: + if (GetDeclaredTypeAssignment(parent) is not {} forParentTypeAssignment || + forParentTypeAssignment.Reference.Type is not ArrayType arrayType) + { + return null; + } - case ForSyntax when parentType is ArrayType arrayType: // the parent is a for-expression // this object is the body of the array, so its declared type is the type of the item // (discriminators have already been resolved when declared type was determined for the for-expression - return TryCreateAssignment(arrayType.Item.Type, syntax, parentTypeAssignment.Flags); + return TryCreateAssignment(arrayType.Item.Type, syntax, forParentTypeAssignment.Flags); + + case ParameterDeclarationSyntax parameterDeclaration: + if (!object.ReferenceEquals(parameterDeclaration.Modifier, syntax) || + GetDeclaredTypeAssignment(parent)?.Reference.Type is not {} paramParent) + { + return null; + } - case ParameterDeclarationSyntax parameterDeclaration when ReferenceEquals(parameterDeclaration.Modifier, syntax): // the object is a modifier of a parameter type // the declared type should be the appropriate modifier type // however we need the parameter's assigned type to determine the modifier type var parameterAssignedType = parameterDeclaration.GetAssignedType(this.typeManager, null); - return TryCreateAssignment(LanguageConstants.CreateParameterModifierType(parentType, parameterAssignedType), syntax); + return TryCreateAssignment(LanguageConstants.CreateParameterModifierType(paramParent, parameterAssignedType), syntax); case ObjectPropertySyntax: + if (GetDeclaredTypeAssignment(parent) is not {} objectPropertyAssignment || + objectPropertyAssignment.Reference.Type is not {} objectPropertyParent) + { + return null; + } + // the object is the value of a property of another object // use the declared type of the property and propagate the flags - return TryCreateAssignment(ResolveDiscriminatedObjects(parentType, syntax), syntax, parentTypeAssignment.Flags); + return TryCreateAssignment(ResolveDiscriminatedObjects(objectPropertyParent, syntax), syntax, objectPropertyAssignment.Flags); case ArrayItemSyntax: + if (GetDeclaredTypeAssignment(parent) is not {} arrayItemAssignment || + arrayItemAssignment.Reference.Type is not {} arrayParent) + { + return null; + } + // the object is an item in an array // use the item's type and propagate flags - return TryCreateAssignment(ResolveDiscriminatedObjects(parentType, syntax), syntax, parentTypeAssignment.Flags); + return TryCreateAssignment(ResolveDiscriminatedObjects(arrayParent, syntax), syntax, arrayItemAssignment.Flags); } return null; @@ -562,14 +602,14 @@ private DeclaredTypeAssignment GetModuleType(ModuleDeclarationSyntax syntax) private static TypeSymbol? ResolveDiscriminatedObjects(TypeSymbol type, ObjectSyntax syntax) { - if (!(type is DiscriminatedObjectType discriminated)) + if (type is not DiscriminatedObjectType discriminated) { // not a discriminated object type - return as-is return type; } var discriminatorProperties = syntax.Properties - .Where(p => string.Equals(p.TryGetKeyText(), discriminated.DiscriminatorKey, LanguageConstants.IdentifierComparison)) + .Where(p => discriminated.TryGetDiscriminatorProperty(p.TryGetKeyText()) is not null) .ToList(); if (discriminatorProperties.Count != 1) @@ -584,7 +624,7 @@ private DeclaredTypeAssignment GetModuleType(ModuleDeclarationSyntax syntax) // for the purposes of resolving the discriminated object, we just need to check if it's a literal string // which doesn't require the full type check, so we're fine var discriminatorProperty = discriminatorProperties.Single(); - if (!(discriminatorProperty.Value is StringSyntax stringSyntax)) + if (discriminatorProperty.Value is not StringSyntax stringSyntax) { // the discriminator property value is not a string return type; diff --git a/src/Bicep.Core/TypeSystem/DiscriminatedObjectType.cs b/src/Bicep.Core/TypeSystem/DiscriminatedObjectType.cs index 638b3d6a801..3c672c9ea06 100644 --- a/src/Bicep.Core/TypeSystem/DiscriminatedObjectType.cs +++ b/src/Bicep.Core/TypeSystem/DiscriminatedObjectType.cs @@ -51,5 +51,23 @@ public DiscriminatedObjectType(string name, TypeSymbolValidationFlags validation public string DiscriminatorKey => this.DiscriminatorProperty.Name; public TypeSymbol DiscriminatorKeysUnionType { get; } + + /// + /// Returns the discriminator property if the given property key matches the discriminator key. + /// + public TypeProperty? TryGetDiscriminatorProperty(string? propertyKey) + { + if (propertyKey is null) + { + return null; + } + + if (LanguageConstants.IdentifierComparer.Equals(propertyKey, DiscriminatorKey)) + { + return DiscriminatorProperty; + } + + return null; + } } } diff --git a/src/Bicep.Core/TypeSystem/FunctionFlags.cs b/src/Bicep.Core/TypeSystem/FunctionFlags.cs index 25d859b4d27..a43d7fbee62 100644 --- a/src/Bicep.Core/TypeSystem/FunctionFlags.cs +++ b/src/Bicep.Core/TypeSystem/FunctionFlags.cs @@ -45,14 +45,19 @@ public enum FunctionFlags /// ModuleDecorator = 1 << 5, + /// + /// The function can be used as an output decorator. + /// + OutputDecorator = 1 << 6, + /// /// The function can be used a resource or module decorator. /// ResourceOrModuleDecorator = ResourceDecorator | ModuleDecorator, /// - /// The function can be used as an output decorator. + /// The function can be used as a decorator anywhere. /// - OutputDecorator = 1 << 6, + AnyDecorator = FunctionFlags.ParameterDecorator | FunctionFlags.VariableDecorator | FunctionFlags.ResourceDecorator | FunctionFlags.ModuleDecorator | FunctionFlags.OutputDecorator, } } diff --git a/src/Bicep.Core/TypeSystem/TypeAssignmentVisitor.cs b/src/Bicep.Core/TypeSystem/TypeAssignmentVisitor.cs index 596f9e7bb85..c6d9f1a1789 100644 --- a/src/Bicep.Core/TypeSystem/TypeAssignmentVisitor.cs +++ b/src/Bicep.Core/TypeSystem/TypeAssignmentVisitor.cs @@ -514,13 +514,30 @@ public override void VisitObjectSyntax(ObjectSyntax syntax) return ErrorType.Create(errors); } + // Discriminated objects should have been resolved by the declared type manager. + var declaredType = typeManager.GetDeclaredType(syntax); + // type results are cached var namedProperties = syntax.Properties .GroupByExcludingNull(p => p.TryGetKeyText(), LanguageConstants.IdentifierComparer) - .Select(group => new TypeProperty(group.Key, UnionType.Create(group.Select(p => typeManager.GetTypeInfo(p))))); + .Select(group => + { + var resolvedType = UnionType.Create(group.Select(p => typeManager.GetTypeInfo(p))); + + if (declaredType is ObjectType objectType && objectType.Properties.TryGetValue(group.Key, out var property)) + { + // we've found a declared object type for the containing object, with a matching property name definition. + // preserve the type property details (name, descriptions etc.), and update the assigned type. + return new TypeProperty(property.Name, resolvedType, property.Flags, property.Description); + } + + // we've not been able to find a declared object type for the containing object, or it doesn't contain a property matching this one. + // best we can do is to simply generate a property for the assigned type. + return new TypeProperty(group.Key, resolvedType); + }); var additionalProperties = syntax.Properties - .Where(p => p.TryGetKeyText() == null) + .Where(p => p.TryGetKeyText() is null) .Select(p => typeManager.GetTypeInfo(p)); var additionalPropertiesType = additionalProperties.Any() ? UnionType.Create(additionalProperties) : null; @@ -893,6 +910,46 @@ public override void VisitFunctionCallSyntax(FunctionCallSyntax syntax) } }); + private Symbol? GetSymbolForDecorator(DecoratorSyntax decorator) + { + if (binder.GetSymbolInfo(decorator.Expression) is {} symbol) + { + return symbol; + } + + if (decorator.Expression is not InstanceFunctionCallSyntax ifc || + typeManager.GetTypeInfo(ifc.BaseExpression) is not NamespaceType namespaceType) + { + return null; + } + + if (!ifc.Name.IsValid) + { + // the parser produced an instance function calls with an invalid name + // all instance function calls must be bound to a symbol, so let's + // bind to a symbol without any errors (there's already a parse error) + return null; + } + + var functionFlags = binder.GetParent(decorator) switch + { + ResourceDeclarationSyntax _ => FunctionFlags.ResourceDecorator, + ModuleDeclarationSyntax _ => FunctionFlags.ModuleDecorator, + ParameterDeclarationSyntax _ => FunctionFlags.ParameterDecorator, + VariableDeclarationSyntax _ => FunctionFlags.VariableDecorator, + OutputDeclarationSyntax _ => FunctionFlags.OutputDecorator, + _ => FunctionFlags.AnyDecorator, + }; + + var resolvedSymbol = functionFlags.HasAnyDecoratorFlag() + // Decorator functions are only valid when HasDecoratorFlag() is true which means + // the instance function call is the top level expression of a DecoratorSyntax node. + ? namespaceType.MethodResolver.TryGetSymbol(ifc.Name) ?? namespaceType.DecoratorResolver.TryGetSymbol(ifc.Name) + : namespaceType.MethodResolver.TryGetSymbol(ifc.Name); + + return SymbolValidator.ResolveNamespaceQualifiedFunction(functionFlags, resolvedSymbol, ifc.Name, namespaceType); + } + public override void VisitInstanceFunctionCallSyntax(InstanceFunctionCallSyntax syntax) => AssignType(syntax, () => { var errors = new List(); @@ -907,7 +964,7 @@ public override void VisitInstanceFunctionCallSyntax(InstanceFunctionCallSyntax baseType = UnwrapType(baseType); - if (!(baseType is ObjectType objectType)) + if (baseType is not ObjectType objectType) { // can only access methods on objects return ErrorType.Create(DiagnosticBuilder.ForPosition(syntax.Name).ObjectRequiredForMethodAccess(baseType)); @@ -918,11 +975,22 @@ public override void VisitInstanceFunctionCallSyntax(InstanceFunctionCallSyntax CollectErrors(errors, argumentType); } - if (this.binder.GetSymbolInfo(syntax) is not Symbol foundSymbol) + if (!syntax.Name.IsValid) { - // namespace methods will have already been bound. Everything else will not have been. - var resolvedSymbol = objectType.MethodResolver.TryGetSymbol(syntax.Name); + // the parser produced an instance function calls with an invalid name + // all instance function calls must be bound to a symbol, so let's + // bind to a symbol without any errors (there's already a parse error) + return ErrorType.Empty(); + } + Symbol? foundSymbol; + if (this.binder.GetParent(syntax) is DecoratorSyntax decorator) + { + foundSymbol = GetSymbolForDecorator(decorator); + } + else + { + var resolvedSymbol = objectType.MethodResolver.TryGetSymbol(syntax.Name); foundSymbol = SymbolValidator.ResolveObjectQualifiedFunction(resolvedSymbol, syntax.Name, objectType); } @@ -979,25 +1047,21 @@ public override void VisitTargetScopeSyntax(TargetScopeSyntax syntax) // There must exist at least one decorator for MissingDeclarationSyntax. var lastDecoratorSyntax = syntax.Decorators.Last(); - if (this.binder.GetSymbolInfo(lastDecoratorSyntax.Expression) is FunctionSymbol functionSymbol) + diagnostics.Write(lastDecoratorSyntax, builder => { - var diagnosticBuilder = DiagnosticBuilder.ForPosition(lastDecoratorSyntax); - var diagnostic = functionSymbol.FunctionFlags switch - { - FunctionFlags.ParameterDecorator => diagnosticBuilder.ExpectedParameterDeclarationAfterDecorator(), - FunctionFlags.VariableDecorator => diagnosticBuilder.ExpectedVariableDeclarationAfterDecorator(), - FunctionFlags.ResourceDecorator => diagnosticBuilder.ExpectedResourceDeclarationAfterDecorator(), - FunctionFlags.ModuleDecorator => diagnosticBuilder.ExpectedModuleDeclarationAfterDecorator(), - FunctionFlags.OutputDecorator => diagnosticBuilder.ExpectedOutputDeclarationAfterDecorator(), - FunctionFlags.ResourceOrModuleDecorator => diagnosticBuilder.ExpectedResourceOrModuleDeclarationAfterDecorator(), - _ => null, - }; + var functionSymbol = this.GetSymbolForDecorator(lastDecoratorSyntax) as FunctionSymbol; - if (diagnostic is not null) + return functionSymbol?.FunctionFlags switch { - diagnostics.Write(diagnostic); - } - } + FunctionFlags.ParameterDecorator => builder.ExpectedParameterDeclarationAfterDecorator(), + FunctionFlags.VariableDecorator => builder.ExpectedVariableDeclarationAfterDecorator(), + FunctionFlags.ResourceDecorator => builder.ExpectedResourceDeclarationAfterDecorator(), + FunctionFlags.ModuleDecorator => builder.ExpectedModuleDeclarationAfterDecorator(), + FunctionFlags.OutputDecorator => builder.ExpectedOutputDeclarationAfterDecorator(), + FunctionFlags.ResourceOrModuleDecorator => builder.ExpectedResourceOrModuleDeclarationAfterDecorator(), + _ => builder.ExpectedDeclarationAfterDecorator(), + }; + }); return LanguageConstants.Any; }); diff --git a/src/Bicep.Core/TypeSystem/TypeProperty.cs b/src/Bicep.Core/TypeSystem/TypeProperty.cs index ebc9068bf49..52f51cb0e3f 100644 --- a/src/Bicep.Core/TypeSystem/TypeProperty.cs +++ b/src/Bicep.Core/TypeSystem/TypeProperty.cs @@ -5,15 +5,18 @@ namespace Bicep.Core.TypeSystem { public class TypeProperty { - public TypeProperty(string name, ITypeReference typeReference, TypePropertyFlags flags = TypePropertyFlags.None) + public TypeProperty(string name, ITypeReference typeReference, TypePropertyFlags flags = TypePropertyFlags.None, string? description = null) { this.Name = name; this.TypeReference = typeReference; this.Flags = flags; + this.Description = description; } public string Name { get; } + public string? Description { get; } + public ITypeReference TypeReference { get; } public TypePropertyFlags Flags { get; } diff --git a/src/Bicep.Core/TypeSystem/TypeValidator.cs b/src/Bicep.Core/TypeSystem/TypeValidator.cs index c27f0c03fd6..17220419ddb 100644 --- a/src/Bicep.Core/TypeSystem/TypeValidator.cs +++ b/src/Bicep.Core/TypeSystem/TypeValidator.cs @@ -260,7 +260,7 @@ private static TypeSymbol NarrowDiscriminatedObjectType(ITypeManager typeManager return LanguageConstants.Any; } - var discriminatorProperty = expression.Properties.FirstOrDefault(x => LanguageConstants.IdentifierComparer.Equals(x.TryGetKeyText(), targetType.DiscriminatorKey)); + var discriminatorProperty = expression.Properties.FirstOrDefault(p => targetType.TryGetDiscriminatorProperty(p.TryGetKeyText()) is not null); if (discriminatorProperty == null) { // object doesn't contain the discriminator field @@ -288,7 +288,7 @@ private static TypeSymbol NarrowDiscriminatedObjectType(ITypeManager typeManager // Let's not do this just yet, and see if a use-case arises. var discriminatorType = typeManager.GetTypeInfo(discriminatorProperty.Value); - if (!(discriminatorType is StringLiteralType stringLiteralDiscriminator)) + if (discriminatorType is not StringLiteralType stringLiteralDiscriminator) { diagnosticWriter.Write(DiagnosticBuilder.ForPosition(expression).PropertyTypeMismatch(ShouldWarn(targetType), targetType.DiscriminatorKey, targetType.DiscriminatorKeysUnionType, discriminatorType)); return LanguageConstants.Any; diff --git a/src/Bicep.LangServer.IntegrationTests/Assertions/AssertionScopeExtensions.cs b/src/Bicep.LangServer.IntegrationTests/Assertions/AssertionScopeExtensions.cs new file mode 100644 index 00000000000..f6c3db0f9ec --- /dev/null +++ b/src/Bicep.LangServer.IntegrationTests/Assertions/AssertionScopeExtensions.cs @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using Bicep.Core.Diagnostics; +using Bicep.Core.Extensions; +using Bicep.Core.Parsing; +using Bicep.Core.Syntax; +using Bicep.Core.Text; +using Bicep.Core.UnitTests.Utils; +using FluentAssertions.Execution; +using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using Range = OmniSharp.Extensions.LanguageServer.Protocol.Models.Range; + +namespace Bicep.LangServer.IntegrationTests.Assertions +{ + public static class AssertionScopeExtensions + { + public static AssertionScope WithAnnotations(this AssertionScope assertionScope, SyntaxTree syntaxTree, string contextName, IEnumerable? data, Func messageFunc, Func rangeFunc) + => Core.UnitTests.Assertions.AssertionScopeExtensions.WithAnnotatedSource( + assertionScope, + syntaxTree, + contextName, + (data ?? Enumerable.Empty()).Select(x => new PrintHelper.Annotation(FromRange(syntaxTree, rangeFunc(x)), messageFunc(x)))); + + private static TextSpan FromRange(SyntaxTree syntaxTree, Range range) + { + var position = TextCoordinateConverter.GetOffset(syntaxTree.LineStarts, range.Start.Line, range.Start.Character); + var length = TextCoordinateConverter.GetOffset(syntaxTree.LineStarts, range.End.Line, range.End.Character) - position; + + return new TextSpan(position, length); + } + } +} diff --git a/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs b/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs index f805cadb6ae..c5ab2e362f4 100644 --- a/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/CompletionTests.cs @@ -199,46 +199,68 @@ public async Task CompletionRequestShouldProduceExpectedCompletions(DataSet data [TestMethod] public async Task String_segments_do_not_return_completions() { - var fileWithCursors = @" + var (file, cursors) = ParserHelper.GetFileWithCursors(@" var completeString = |'he|llo'| var interpolatedString = |'abc${|true}|de|f${|false}|gh|i'| var multilineString = |'''| hel|lo '''| -"; - var bicepFile = fileWithCursors.Replace("|", ""); - var syntaxTree = SyntaxTree.Create(new Uri("file:///main.bicep"), bicepFile); +"); - var cursors = new List(); - for (var i = 0; i < fileWithCursors.Length; i++) - { - if (fileWithCursors[i] == '|') - { - cursors.Add(i - cursors.Count); - } - } - - using var client = await IntegrationTestHelper.StartServerWithTextAsync(bicepFile, syntaxTree.FileUri, resourceTypeProvider: TypeProvider); + var syntaxTree = SyntaxTree.Create(new Uri("file:///main.bicep"), file); + using var client = await IntegrationTestHelper.StartServerWithTextAsync(file, syntaxTree.FileUri, resourceTypeProvider: TypeProvider); foreach (var cursor in cursors) { - using var assertionScope = new AssertionScope(); - assertionScope.AddReportable( - "completion context", - PrintHelper.PrintWithAnnotations(syntaxTree, new[] { - new PrintHelper.Annotation(new TextSpan(cursor, 0), "cursor position"), - }, 1, true)); - - var completions = await client.RequestCompletion(new CompletionParams + using (new AssertionScope().WithVisualCursor(syntaxTree, new TextSpan(cursor, 0))) { - TextDocument = new TextDocumentIdentifier(syntaxTree.FileUri), - Position = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, cursor), - }); + var completions = await client.RequestCompletion(new CompletionParams + { + TextDocument = new TextDocumentIdentifier(syntaxTree.FileUri), + Position = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, cursor), + }); - completions.Should().BeEmpty(); + completions.Should().BeEmpty(); + } } } + [TestMethod] + public async Task Property_completions_include_descriptions() + { + var (file, cursors) = ParserHelper.GetFileWithCursors(@" +resource testRes 'Test.Rp/readWriteTests@2020-01-01' = { + name: 'testRes' + properties: { + | + } +} + +output string test = testRes.| +output string test2 = testRes.properties.| +"); + + var syntaxTree = SyntaxTree.Create(new Uri("file:///path/to/main.bicep"), file); + var client = await IntegrationTestHelper.StartServerWithTextAsync(file, syntaxTree.FileUri, resourceTypeProvider: BuiltInTestTypes.Create()); + var completions = await RequestCompletions(client, syntaxTree, cursors); + + completions.Should().SatisfyRespectively( + x => x!.OrderBy(d => d.SortText).Should().SatisfyRespectively( + d => d.Documentation!.MarkupContent!.Value.Should().Contain("This is a property which supports reading AND writing!"), + d => d.Documentation!.MarkupContent!.Value.Should().Contain("This is a property which is required."), + d => d.Documentation!.MarkupContent!.Value.Should().Contain("This is a property which only supports writing.")), + x => x!.OrderBy(d => d.SortText).Should().SatisfyRespectively( + d => d.Documentation!.MarkupContent!.Value.Should().Contain("apiVersion property"), + d => d.Documentation!.MarkupContent!.Value.Should().Contain("id property"), + d => d.Documentation!.MarkupContent!.Value.Should().Contain("name property"), + d => d.Documentation!.MarkupContent!.Value.Should().Contain("properties property"), + d => d.Documentation!.MarkupContent!.Value.Should().Contain("type property")), + x => x!.OrderBy(d => d.SortText).Should().SatisfyRespectively( + d => d.Documentation!.MarkupContent!.Value.Should().Contain("This is a property which only supports reading."), + d => d.Documentation!.MarkupContent!.Value.Should().Contain("This is a property which supports reading AND writing!"), + d => d.Documentation!.MarkupContent!.Value.Should().Contain("This is a property which is required."))); + } + private void ValidateCompletions(DataSet dataSet, string setName, List<(Position position, JToken actual)> intermediate) { // if the test author asserts on the wrong completion set in their tests @@ -419,5 +441,22 @@ public CompletionData(string prefix, string snippetText) public static string GetDisplayName(MethodInfo methodInfo, object[] data) => ((CompletionData)data[0]).Prefix!; } + + private static async Task> RequestCompletions(ILanguageClient client, SyntaxTree syntaxTree, IEnumerable cursors) + { + var completions = new List(); + foreach (var cursor in cursors) + { + var completionList = await client.RequestCompletion(new CompletionParams + { + TextDocument = new TextDocumentIdentifier(syntaxTree.FileUri), + Position = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, cursor), + }); + + completions.Add(completionList); + } + + return completions; + } } } diff --git a/src/Bicep.LangServer.IntegrationTests/DefinitionTests.cs b/src/Bicep.LangServer.IntegrationTests/DefinitionTests.cs index 8eed9fa17e8..b82738b61e3 100644 --- a/src/Bicep.LangServer.IntegrationTests/DefinitionTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/DefinitionTests.cs @@ -14,10 +14,12 @@ using Bicep.LanguageServer.Extensions; using Bicep.LanguageServer.Utils; using FluentAssertions; +using FluentAssertions.Execution; using Microsoft.VisualStudio.TestTools.UnitTesting; using OmniSharp.Extensions.LanguageServer.Protocol; using OmniSharp.Extensions.LanguageServer.Protocol.Document; using OmniSharp.Extensions.LanguageServer.Protocol.Models; +using Bicep.Core.UnitTests.Assertions; namespace Bicep.LangServer.IntegrationTests { @@ -81,7 +83,7 @@ public async Task GoToDefinitionRequestOnUnsupportedOrInvalidSyntaxNodeShouldRet var symbolTable = compilation.ReconstructSymbolTable(); var lineStarts = compilation.SyntaxTreeGrouping.EntryPoint.LineStarts; - var undeclaredSymbolBindings = symbolTable.Where(pair => !(pair.Value is DeclaredSymbol)); + var undeclaredSymbolBindings = symbolTable.Where(pair => pair.Value is not DeclaredSymbol and not PropertySymbol); foreach (var (syntax, _) in undeclaredSymbolBindings) { @@ -91,9 +93,12 @@ public async Task GoToDefinitionRequestOnUnsupportedOrInvalidSyntaxNodeShouldRet Position = PositionHelper.GetPosition(lineStarts, syntax.Span.Position) }); - // go to definition on a symbol that isn't declared by the user (like error or function symbol) - // should produce an empty response - response.Should().BeEmpty(); + using (new AssertionScope().WithVisualCursor(compilation.SyntaxTreeGrouping.EntryPoint, syntax.Span)) + { + // go to definition on a symbol that isn't declared by the user (like error or function symbol) + // should produce an empty response + response.Should().BeEmpty(); + } } } diff --git a/src/Bicep.LangServer.IntegrationTests/Extensions/CompilationTestExtensions.cs b/src/Bicep.LangServer.IntegrationTests/Extensions/CompilationTestExtensions.cs index d9c35b979fe..37bc55b781b 100644 --- a/src/Bicep.LangServer.IntegrationTests/Extensions/CompilationTestExtensions.cs +++ b/src/Bicep.LangServer.IntegrationTests/Extensions/CompilationTestExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the MIT License. using System.Collections.Generic; -using System.Linq; using Bicep.Core.Semantics; using Bicep.Core.Syntax; using Bicep.Core.Syntax.Visitors; @@ -15,15 +14,15 @@ public static class CompilationTestExtensions { var model = compilation.GetEntrypointSemanticModel(); - var syntaxNodes = SyntaxAggregator.Aggregate(compilation.SyntaxTreeGrouping.EntryPoint.ProgramSyntax, new List(), (accumulated, node) => + return SyntaxAggregator.Aggregate(compilation.SyntaxTreeGrouping.EntryPoint.ProgramSyntax, new Dictionary(), (accumulated, node) => { - accumulated.Add(node); + if (model.GetSymbolInfo(node) is Symbol symbol) + { + accumulated[node] = symbol; + } + return accumulated; }, accumulated => accumulated); - - return syntaxNodes - .Where(syntax => model.GetSymbolInfo(syntax) != null) - .ToDictionary(syntax => syntax, syntax => model.GetSymbolInfo(syntax)!); } } } diff --git a/src/Bicep.LangServer.IntegrationTests/HighlightTests.cs b/src/Bicep.LangServer.IntegrationTests/HighlightTests.cs index 3edfa02d023..c10a548ce60 100644 --- a/src/Bicep.LangServer.IntegrationTests/HighlightTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/HighlightTests.cs @@ -11,9 +11,11 @@ using Bicep.Core.Semantics; using Bicep.Core.Syntax; using Bicep.Core.Syntax.Visitors; +using Bicep.LangServer.IntegrationTests.Assertions; using Bicep.LangServer.IntegrationTests.Extensions; using Bicep.LanguageServer.Utils; using FluentAssertions; +using FluentAssertions.Execution; using Microsoft.VisualStudio.TestTools.UnitTesting; using OmniSharp.Extensions.LanguageServer.Protocol; using OmniSharp.Extensions.LanguageServer.Protocol.Document; @@ -44,6 +46,8 @@ public async Task HighlightsShouldShowAllReferencesOfTheSymbol(DataSet dataSet) // (locals are special because their full span is the same as the identifier span, // which makes it impossible to highlight locals with invalid identifiers) var filteredSymbolTable = symbolTable.Where(pair => pair.Value.Kind != SymbolKind.Error && (pair.Value is not LocalVariableSymbol local || local.NameSyntax.IsValid)); + // TODO: Implement for PropertySymbol + filteredSymbolTable = filteredSymbolTable.Where(pair => pair.Value is not PropertySymbol); var symbolToSyntaxLookup = filteredSymbolTable.ToLookup(pair => pair.Value, pair => pair.Key); @@ -58,8 +62,13 @@ public async Task HighlightsShouldShowAllReferencesOfTheSymbol(DataSet dataSet) // calculate expected highlights var expectedHighlights = symbolToSyntaxLookup[symbol].Select(node => CreateExpectedHighlight(lineStarts, node)); - // ranges should match what we got from our own symbol table - highlights.Should().BeEquivalentTo(expectedHighlights); + using (new AssertionScope() + .WithAnnotations(compilation.SyntaxTreeGrouping.EntryPoint, "expected", expectedHighlights, _ => "here", x => x.Range) + .WithAnnotations(compilation.SyntaxTreeGrouping.EntryPoint, "actual", highlights, _ => "here", x => x.Range)) + { + // ranges should match what we got from our own symbol table + highlights.Should().BeEquivalentTo(expectedHighlights); + } } } @@ -118,10 +127,12 @@ private static DocumentHighlightKind GetExpectedHighlightKind(SyntaxBase syntax) { switch (syntax) { - case ISymbolReference _: + case ISymbolReference: + case PropertyAccessSyntax: return DocumentHighlightKind.Read; - case INamedDeclarationSyntax _: + case INamedDeclarationSyntax: + case ObjectPropertySyntax: return DocumentHighlightKind.Write; default: diff --git a/src/Bicep.LangServer.IntegrationTests/HoverTests.cs b/src/Bicep.LangServer.IntegrationTests/HoverTests.cs index 6cd9e3a931b..35b8f8b852c 100644 --- a/src/Bicep.LangServer.IntegrationTests/HoverTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/HoverTests.cs @@ -1,8 +1,10 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Threading.Tasks; using Azure.Bicep.Types.Az; using Bicep.Core.Extensions; @@ -14,15 +16,16 @@ using Bicep.Core.Syntax.Visitors; using Bicep.Core.Text; using Bicep.Core.TypeSystem.Az; +using Bicep.Core.UnitTests.Assertions; using Bicep.Core.UnitTests.Utils; using Bicep.LangServer.IntegrationTests.Assertions; using Bicep.LangServer.IntegrationTests.Extensions; using Bicep.LangServer.IntegrationTests.Helpers; -using Bicep.LanguageServer.Utils; using FluentAssertions; using FluentAssertions.Execution; using Microsoft.VisualStudio.TestTools.UnitTesting; using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Client; using OmniSharp.Extensions.LanguageServer.Protocol.Document; using OmniSharp.Extensions.LanguageServer.Protocol.Models; using SymbolKind = Bicep.Core.Semantics.SymbolKind; @@ -36,20 +39,6 @@ public class HoverTests [NotNull] public TestContext? TestContext { get; set; } - private static IAssertionScope CreateAssertionScopeWithContext(SyntaxTree syntaxTree, Hover? hover, IPositionable requestedPosition) - { - var assertionScope = new AssertionScope(); - - // TODO: figure out how to set this only on failure, rather than always calculating it - assertionScope.AddReportable( - "hover context", - PrintHelper.PrintWithAnnotations(syntaxTree, new [] { - new PrintHelper.Annotation(requestedPosition.Span, "cursor position"), - }, 1, true)); - - return assertionScope; - } - [DataTestMethod] [DynamicData(nameof(GetData), DynamicDataSourceType.Method, DynamicDataDisplayNameDeclaringType = typeof(DataSet), DynamicDataDisplayName = nameof(DataSet.GetDisplayName))] public async Task HoveringOverSymbolReferencesAndDeclarationsShouldProduceHovers(DataSet dataSet) @@ -77,26 +66,36 @@ public async Task HoveringOverSymbolReferencesAndDeclarationsShouldProduceHovers }, accumulated => accumulated); - foreach (SyntaxBase symbolReference in symbolReferences) + foreach (var symbolReference in symbolReferences) { + // by default, request a hover on the first character of the syntax, but for certain syntaxes, this doesn't make sense. + // for example on an instance function call 'az.resourceGroup()', it only makes sense to request a hover on the 3rd character. var nodeForHover = symbolReference switch { ITopLevelDeclarationSyntax d => d.Keyword, ResourceAccessSyntax r => r.ResourceName, + FunctionCallSyntaxBase f => f.Name, _ => symbolReference, }; var hover = await client.RequestHover(new HoverParams { TextDocument = new TextDocumentIdentifier(uri), - Position = PositionHelper.GetPosition(lineStarts, nodeForHover.Span.Position) + Position = TextCoordinateConverter.GetPosition(lineStarts, nodeForHover.Span.Position) }); // fancy method to give us some annotated source code to look at if any assertions fail :) - using (CreateAssertionScopeWithContext(compilation.SyntaxTreeGrouping.EntryPoint, hover, nodeForHover.Span.ToZeroLengthSpan())) + using (new AssertionScope().WithVisualCursor(compilation.SyntaxTreeGrouping.EntryPoint, nodeForHover.Span.ToZeroLengthSpan())) { - if (symbolTable.TryGetValue(symbolReference, out var symbol) == false) + if (!symbolTable.TryGetValue(symbolReference, out var symbol)) { + if (symbolReference is InstanceFunctionCallSyntax && + compilation.GetEntrypointSemanticModel().GetSymbolInfo(symbolReference) is FunctionSymbol ifcSymbol) + { + ValidateHover(hover, ifcSymbol); + break; + } + // symbol ref not bound to a symbol hover.Should().BeNull(); continue; @@ -109,15 +108,6 @@ public async Task HoveringOverSymbolReferencesAndDeclarationsShouldProduceHovers hover.Should().BeNull(); break; - // when a namespace value is found and there was an error with the function call or - // is a valid function call or namespace access, all these cases will have a hover range - // with some text - case SymbolKind.Error when symbolReference is InstanceFunctionCallSyntax: - case SymbolKind.Function when symbolReference is InstanceFunctionCallSyntax: - case SymbolKind.Namespace: - ValidateInstanceFunctionCallHover(hover); - break; - case SymbolKind.Error: // error symbol hover.Should().BeNull(); @@ -171,17 +161,127 @@ public async Task HoveringOverNonHoverableElementsShouldProduceEmptyHovers(DataS var hover = await client.RequestHover(new HoverParams { TextDocument = new TextDocumentIdentifier(uri), - Position = PositionHelper.GetPosition(lineStarts, node.Span.Position) + Position = TextCoordinateConverter.GetPosition(lineStarts, node.Span.Position) }); // fancy method to give us some annotated source code to look at if any assertions fail :) - using (CreateAssertionScopeWithContext(compilation.SyntaxTreeGrouping.EntryPoint, hover, node.Span.ToZeroLengthSpan())) + using (new AssertionScope().WithVisualCursor(compilation.SyntaxTreeGrouping.EntryPoint, node.Span.ToZeroLengthSpan())) { hover.Should().BeNull(); } } } + + [DataTestMethod] + public async Task PropertyHovers_are_displayed_on_properties() + { + var (file, cursors) = ParserHelper.GetFileWithCursors(@" +resource testRes 'Test.Rp/readWriteTests@2020-01-01' = { + n|ame: 'testRes' + prop|erties: { + readwri|te: 'abc' + write|only: 'def' + requ|ired: 'ghi' + } +} + +output string test = testRes.prop|erties.rea|donly +"); + + var syntaxTree = SyntaxTree.Create(new Uri("file:///path/to/main.bicep"), file); + var client = await IntegrationTestHelper.StartServerWithTextAsync(file, syntaxTree.FileUri, resourceTypeProvider: BuiltInTestTypes.Create()); + var hovers = await RequestHovers(client, syntaxTree, cursors); + + hovers.Should().SatisfyRespectively( + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nname: string\n```\nname property\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nproperties: Properties\n```\nproperties property\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nreadwrite: string\n```\nThis is a property which supports reading AND writing!\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nwriteonly: string\n```\nThis is a property which only supports writing.\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nrequired: string\n```\nThis is a property which is required.\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nproperties: Properties\n```\nproperties property\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nreadonly: string\n```\nThis is a property which only supports reading.\n")); + } + + + [DataTestMethod] + public async Task PropertyHovers_are_displayed_on_properties_with_loops() + { + var (file, cursors) = ParserHelper.GetFileWithCursors(@" +resource testRes 'Test.Rp/readWriteTests@2020-01-01' = [for i in range(0, 10): { + n|ame: 'testRes${i}' + prop|erties: { + readwri|te: 'abc' + write|only: 'def' + requ|ired: 'ghi' + } +}] + +output string test = testRes[3].prop|erties.rea|donly +"); + + var syntaxTree = SyntaxTree.Create(new Uri("file:///path/to/main.bicep"), file); + var client = await IntegrationTestHelper.StartServerWithTextAsync(file, syntaxTree.FileUri, resourceTypeProvider: BuiltInTestTypes.Create()); + var hovers = await RequestHovers(client, syntaxTree, cursors); + + hovers.Should().SatisfyRespectively( + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nname: string\n```\nname property\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nproperties: Properties\n```\nproperties property\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nreadwrite: string\n```\nThis is a property which supports reading AND writing!\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nwriteonly: string\n```\nThis is a property which only supports writing.\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nrequired: string\n```\nThis is a property which is required.\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nproperties: Properties\n```\nproperties property\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nreadonly: string\n```\nThis is a property which only supports reading.\n")); + } + + + [DataTestMethod] + public async Task PropertyHovers_are_displayed_on_properties_with_conditions() + { + var (file, cursors) = ParserHelper.GetFileWithCursors(@" +resource testRes 'Test.Rp/readWriteTests@2020-01-01' = if (true) { + n|ame: 'testRes' + prop|erties: { + readwri|te: 'abc' + write|only: 'def' + requ|ired: 'ghi' + } +} + +output string test = testRes.prop|erties.rea|donly +"); + + var syntaxTree = SyntaxTree.Create(new Uri("file:///path/to/main.bicep"), file); + var client = await IntegrationTestHelper.StartServerWithTextAsync(file, syntaxTree.FileUri, resourceTypeProvider: BuiltInTestTypes.Create()); + var hovers = await RequestHovers(client, syntaxTree, cursors); + + hovers.Should().SatisfyRespectively( + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nname: string\n```\nname property\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nproperties: Properties\n```\nproperties property\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nreadwrite: string\n```\nThis is a property which supports reading AND writing!\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nwriteonly: string\n```\nThis is a property which only supports writing.\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nrequired: string\n```\nThis is a property which is required.\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nproperties: Properties\n```\nproperties property\n"), + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nreadonly: string\n```\nThis is a property which only supports reading.\n")); + } + + [DataTestMethod] + public async Task PropertyHovers_are_displayed_on_partial_discriminator_objects() + { + var (file, cursors) = ParserHelper.GetFileWithCursors(@" +resource testRes 'Test.Rp/discriminatorTests@2020-01-01' = { + ki|nd +} +"); + + var syntaxTree = SyntaxTree.Create(new Uri("file:///path/to/main.bicep"), file); + var client = await IntegrationTestHelper.StartServerWithTextAsync(file, syntaxTree.FileUri, resourceTypeProvider: BuiltInTestTypes.Create()); + var hovers = await RequestHovers(client, syntaxTree, cursors); + + hovers.Should().SatisfyRespectively( + h => h!.Contents.MarkupContent!.Value.Should().Be("```bicep\nkind: 'BodyA' | 'BodyB'\n```\n")); + } + private static void ValidateHover(Hover? hover, Symbol symbol) { hover.Should().NotBeNull(); @@ -229,30 +329,35 @@ private static void ValidateHover(Hover? hover, Symbol symbol) hover.Contents.MarkupContent.Value.Should().Contain($"{local.Name}: {local.Type}"); break; + case NamespaceSymbol @namespace: + hover.Contents.MarkupContent.Value.Should().Contain($"{@namespace.Name} namespace"); + break; + default: throw new AssertFailedException($"Unexpected symbol type '{symbol.GetType().Name}'"); } } - private static void ValidateInstanceFunctionCallHover(Hover? hover) + private static IEnumerable GetData() { - hover.Should().NotBeNull(); - hover!.Range!.Should().NotBeNull(); - hover.Contents.Should().NotBeNull(); - - hover.Contents.HasMarkedStrings.Should().BeFalse(); - hover.Contents.HasMarkupContent.Should().BeTrue(); - hover.Contents.MarkedStrings.Should().BeNull(); - hover.Contents.MarkupContent.Should().NotBeNull(); - - hover.Contents.MarkupContent!.Kind.Should().Be(MarkupKind.Markdown); - hover.Contents.MarkupContent.Value.Should().StartWith("```bicep\n"); - hover.Contents.MarkupContent.Value.Should().EndWith("```"); + return DataSets.NonStressDataSets.ToDynamicTestData(); } - private static IEnumerable GetData() + private static async Task> RequestHovers(ILanguageClient client, SyntaxTree syntaxTree, IEnumerable cursors) { - return DataSets.NonStressDataSets.ToDynamicTestData(); + var hovers = new List(); + foreach (var cursor in cursors) + { + var hover = await client.RequestHover(new HoverParams + { + TextDocument = new TextDocumentIdentifier(syntaxTree.FileUri), + Position = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, cursor), + }); + + hovers.Add(hover); + } + + return hovers; } } } diff --git a/src/Bicep.LangServer.IntegrationTests/ReferencesTests.cs b/src/Bicep.LangServer.IntegrationTests/ReferencesTests.cs index 6988dcfb968..9c4fea04ab5 100644 --- a/src/Bicep.LangServer.IntegrationTests/ReferencesTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/ReferencesTests.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; @@ -12,12 +13,16 @@ using Bicep.Core.Syntax; using Bicep.Core.Syntax.Visitors; using Bicep.Core.Text; +using Bicep.Core.UnitTests.Assertions; using Bicep.Core.UnitTests.Utils; +using Bicep.LangServer.IntegrationTests.Assertions; using Bicep.LangServer.IntegrationTests.Extensions; using Bicep.LanguageServer.Utils; using FluentAssertions; +using FluentAssertions.Execution; using Microsoft.VisualStudio.TestTools.UnitTesting; using OmniSharp.Extensions.LanguageServer.Protocol; +using OmniSharp.Extensions.LanguageServer.Protocol.Client; using OmniSharp.Extensions.LanguageServer.Protocol.Document; using OmniSharp.Extensions.LanguageServer.Protocol.Models; using SymbolKind = Bicep.Core.Semantics.SymbolKind; @@ -45,6 +50,8 @@ public async Task FindReferencesWithDeclarationsShouldProduceCorrectResults(Data // filter out bind failures and locals with invalid identifiers // (locals are special because their span is equal to their identifier span) var filteredSymbolTable = symbolTable.Where(pair => pair.Value.Kind != SymbolKind.Error && (pair.Value is not LocalVariableSymbol local || local.NameSyntax.IsValid)); + // TODO: Implement for PropertySymbol + filteredSymbolTable = filteredSymbolTable.Where(pair => pair.Value is not PropertySymbol); var symbolToSyntaxLookup = filteredSymbolTable.ToLookup(pair => pair.Value, pair => pair.Key); foreach (var (syntax, symbol) in filteredSymbolTable) @@ -66,8 +73,13 @@ public async Task FindReferencesWithDeclarationsShouldProduceCorrectResults(Data var expectedRanges = symbolToSyntaxLookup[symbol] .Select(node => PositionHelper.GetNameRange(lineStarts, node)); - // ranges should match what we got from our own symbol table - locations.Select(l => l.Range).Should().BeEquivalentTo(expectedRanges); + using (new AssertionScope() + .WithAnnotations(compilation.SyntaxTreeGrouping.EntryPoint, "expected", expectedRanges, _ => "here", x => x) + .WithAnnotations(compilation.SyntaxTreeGrouping.EntryPoint, "actual", locations, _ => "here", x => x.Range)) + { + // ranges should match what we got from our own symbol table + locations.Select(l => l.Range).Should().BeEquivalentTo(expectedRanges); + } } } @@ -85,6 +97,8 @@ public async Task FindReferencesWithoutDeclarationsShouldProduceCorrectResults(D // filter out bind failures and locals with invalid identifiers // (locals are special because their span is equal to their identifier span) var filteredSymbolTable = symbolTable.Where(pair => pair.Value.Kind != SymbolKind.Error && (pair.Value is not LocalVariableSymbol local || local.NameSyntax.IsValid)); + // TODO: Implement for PropertySymbol + filteredSymbolTable = filteredSymbolTable.Where(pair => pair.Value is not PropertySymbol); var symbolToSyntaxLookup = filteredSymbolTable.ToLookup(pair => pair.Value, pair => pair.Key); foreach (var (syntax, symbol) in filteredSymbolTable) @@ -107,8 +121,13 @@ public async Task FindReferencesWithoutDeclarationsShouldProduceCorrectResults(D .Where(node => !(node is INamedDeclarationSyntax)) .Select(node => PositionHelper.GetNameRange(lineStarts, node)); - // ranges should match what we got from our own symbol table - locations.Select(l => l.Range).Should().BeEquivalentTo(expectedRanges); + using (new AssertionScope() + .WithAnnotations(compilation.SyntaxTreeGrouping.EntryPoint, "expected", expectedRanges, _ => "here", x => x) + .WithAnnotations(compilation.SyntaxTreeGrouping.EntryPoint, "actual", locations, _ => "here", x => x.Range)) + { + // ranges should match what we got from our own symbol table + locations.Select(l => l.Range).Should().BeEquivalentTo(expectedRanges); + } } } @@ -160,9 +179,57 @@ public async Task FindReferencesOnNonSymbolsShouldProduceEmptyResult(DataSet dat } } + [TestMethod] + public async Task FindReferences_displays_references_on_namespaced_and_non_namespaced_methods() + { + var (file, cursors) = ParserHelper.GetFileWithCursors(@" +var rg1 = resourc|eGroup().location + +var rg2 = az.r|esourceGroup() + +var rg3 = resourceGr|oup().id + +var dep1 = az.depl|oyment() + +var dep2 = az.deploy|ment() +"); + + var syntaxTree = SyntaxTree.Create(new Uri("file:///path/to/main.bicep"), file); + var client = await IntegrationTestHelper.StartServerWithTextAsync(file, syntaxTree.FileUri, resourceTypeProvider: BuiltInTestTypes.Create()); + var references = await RequestReferences(client, syntaxTree, cursors); + + references.Should().SatisfyRespectively( + r => r.Should().HaveCount(3), + r => r.Should().HaveCount(3), + r => r.Should().HaveCount(3), + r => r.Should().HaveCount(2), + r => r.Should().HaveCount(2)); + } + private static IEnumerable GetData() { return DataSets.NonStressDataSets.ToDynamicTestData(); } + + private static async Task> RequestReferences(ILanguageClient client, SyntaxTree syntaxTree, IEnumerable cursors) + { + var references = new List(); + foreach (var cursor in cursors) + { + var referenceList = await client.RequestReferences(new ReferenceParams + { + TextDocument = new TextDocumentIdentifier(syntaxTree.FileUri), + Context = new ReferenceContext + { + IncludeDeclaration = false + }, + Position = TextCoordinateConverter.GetPosition(syntaxTree.LineStarts, cursor), + }); + + references.Add(referenceList); + } + + return references; + } } } diff --git a/src/Bicep.LangServer.IntegrationTests/SignatureHelpTests.cs b/src/Bicep.LangServer.IntegrationTests/SignatureHelpTests.cs index 1bc0a5530f1..78ddf318a5b 100644 --- a/src/Bicep.LangServer.IntegrationTests/SignatureHelpTests.cs +++ b/src/Bicep.LangServer.IntegrationTests/SignatureHelpTests.cs @@ -6,13 +6,17 @@ using System.Linq; using System.Threading.Tasks; using Bicep.Core.Extensions; +using Bicep.Core.Parsing; using Bicep.Core.Samples; using Bicep.Core.Semantics; using Bicep.Core.Syntax; using Bicep.Core.Syntax.Visitors; +using Bicep.Core.UnitTests.Assertions; +using Bicep.Core.UnitTests.Utils; using Bicep.LangServer.IntegrationTests.Extensions; using Bicep.LanguageServer.Utils; using FluentAssertions; +using FluentAssertions.Execution; using Microsoft.VisualStudio.TestTools.UnitTesting; using OmniSharp.Extensions.LanguageServer.Protocol; using OmniSharp.Extensions.LanguageServer.Protocol.Client; @@ -57,17 +61,17 @@ public async Task ShouldProvideSignatureHelpBetweenFunctionParentheses(DataSet d { var expectDecorator = compilation.GetEntrypointSemanticModel().Binder.GetParent(functionCall) is DecoratorSyntax; - symbolTable.TryGetValue(functionCall, out var symbol); + var symbol = compilation.GetEntrypointSemanticModel().GetSymbolInfo(functionCall); // if the cursor is present immediate after the function argument opening paren, // the signature help can only show the signature of the enclosing function var startOffset = functionCall.OpenParen.GetEndPosition(); - await ValidateOffset(client, uri, tree, startOffset, symbol as FunctionSymbol, expectDecorator); + await ValidateOffset(compilation, client, uri, tree, startOffset, symbol as FunctionSymbol, expectDecorator); // if the cursor is present immediately before the function argument closing paren, // the signature help can only show the signature of the enclosing function var endOffset = functionCall.CloseParen.Span.Position; - await ValidateOffset(client, uri, tree, endOffset, symbol as FunctionSymbol, expectDecorator); + await ValidateOffset(compilation, client, uri, tree, endOffset, symbol as FunctionSymbol, expectDecorator); } } @@ -109,44 +113,51 @@ public async Task NonFunctionCallSyntaxShouldProvideNoSignatureHelp(DataSet data foreach (var nonFunction in nonFunctions) { - var position = PositionHelper.GetPosition(tree.LineStarts, nonFunction.Span.Position); - var signatureHelp = await RequestSignatureHelp(client, position, uri); - signatureHelp.Should().BeNull(); + using (new AssertionScope().WithVisualCursor(tree, nonFunction.Span.ToZeroLengthSpan())) + { + var position = PositionHelper.GetPosition(tree.LineStarts, nonFunction.Span.Position); + var signatureHelp = await RequestSignatureHelp(client, position, uri); + signatureHelp.Should().BeNull(); + } } } - private static async Task ValidateOffset(ILanguageClient client, DocumentUri uri, SyntaxTree tree, int offset, FunctionSymbol? symbol, bool expectDecorator) + private static async Task ValidateOffset(Compilation compilation, ILanguageClient client, DocumentUri uri, SyntaxTree tree, int offset, FunctionSymbol? symbol, bool expectDecorator) { var position = PositionHelper.GetPosition(tree.LineStarts, offset); var initial = await RequestSignatureHelp(client, position, uri); - if(symbol != null) + // fancy method to give us some annotated source code to look at if any assertions fail :) + using (new AssertionScope().WithVisualCursor(tree, new TextSpan(offset, 0))) { - // real function should have valid signature help - AssertValidSignatureHelp(initial, symbol, expectDecorator); - - if (initial!.Signatures.Count() >= 2) + if (symbol is not null) { - // update index to 1 to mock user changing active signature - initial.ActiveSignature = 1; + // real function should have valid signature help + AssertValidSignatureHelp(initial, symbol, expectDecorator); - var shouldRemember = await RequestSignatureHelp(client, position, uri, new SignatureHelpContext + if (initial!.Signatures.Count() >= 2) { - ActiveSignatureHelp = initial, - IsRetrigger = true, - TriggerKind = SignatureHelpTriggerKind.ContentChange - }); - - // we passed the same signature help as content with a different active index - // should get the same index back - AssertValidSignatureHelp(shouldRemember, symbol, expectDecorator); - shouldRemember!.ActiveSignature.Should().Be(1); + // update index to 1 to mock user changing active signature + initial.ActiveSignature = 1; + + var shouldRemember = await RequestSignatureHelp(client, position, uri, new SignatureHelpContext + { + ActiveSignatureHelp = initial, + IsRetrigger = true, + TriggerKind = SignatureHelpTriggerKind.ContentChange + }); + + // we passed the same signature help as content with a different active index + // should get the same index back + AssertValidSignatureHelp(shouldRemember, symbol, expectDecorator); + shouldRemember!.ActiveSignature.Should().Be(1); + } + } + else + { + // not a real function - no signature help expected + initial.Should().BeNull(); } - } - else - { - // not a real function - no signature help expected - initial.Should().BeNull(); } } diff --git a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs index e830ba3b9b3..3d62cabba86 100644 --- a/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs +++ b/src/Bicep.LangServer/Completions/BicepCompletionProvider.cs @@ -973,6 +973,11 @@ private static string FormatPropertyDocumentation(TypeProperty property) buffer.Append($"Requires a compile-time constant value.{MarkdownNewLine}"); } + if (property.Description is not null) + { + buffer.Append($"{property.Description}{MarkdownNewLine}"); + } + return buffer.ToString(); } } diff --git a/src/Bicep.LangServer/Handlers/BicepDefinitionHandler.cs b/src/Bicep.LangServer/Handlers/BicepDefinitionHandler.cs index b5a7f79a535..4a97cbbadc0 100644 --- a/src/Bicep.LangServer/Handlers/BicepDefinitionHandler.cs +++ b/src/Bicep.LangServer/Handlers/BicepDefinitionHandler.cs @@ -29,6 +29,12 @@ public override Task Handle(DefinitionParams request, C return Task.FromResult(new LocationOrLocationLinks()); } + if (result.Symbol is PropertySymbol) + { + // TODO: Implement for PropertySymbol + return Task.FromResult(new LocationOrLocationLinks()); + } + if (result.Symbol is DeclaredSymbol declaration) { return Task.FromResult(new LocationOrLocationLinks(new LocationOrLocationLink(new LocationLink diff --git a/src/Bicep.LangServer/Handlers/BicepDocumentHighlightHandler.cs b/src/Bicep.LangServer/Handlers/BicepDocumentHighlightHandler.cs index ee7f9bd577e..535b8d78f90 100644 --- a/src/Bicep.LangServer/Handlers/BicepDocumentHighlightHandler.cs +++ b/src/Bicep.LangServer/Handlers/BicepDocumentHighlightHandler.cs @@ -4,6 +4,7 @@ using System.Threading; using System.Threading.Tasks; using Bicep.Core.Navigation; +using Bicep.Core.Syntax; using Bicep.LanguageServer.Providers; using Bicep.LanguageServer.Utils; using OmniSharp.Extensions.LanguageServer.Protocol.Document; @@ -33,9 +34,11 @@ public BicepDocumentHighlightHandler(ISymbolResolver symbolResolver) : base(Crea .Select(referenceSyntax => new DocumentHighlight { Range = PositionHelper.GetNameRange(result.Context.LineStarts, referenceSyntax), - Kind = referenceSyntax is INamedDeclarationSyntax - ? DocumentHighlightKind.Write - : DocumentHighlightKind.Read + Kind = referenceSyntax switch { + INamedDeclarationSyntax _ => DocumentHighlightKind.Write, + ObjectPropertySyntax _ => DocumentHighlightKind.Write, + _ => DocumentHighlightKind.Read, + }, }); return Task.FromResult(new DocumentHighlightContainer(highlights)); diff --git a/src/Bicep.LangServer/Handlers/BicepHoverHandler.cs b/src/Bicep.LangServer/Handlers/BicepHoverHandler.cs index 6bb78908901..181979362e1 100644 --- a/src/Bicep.LangServer/Handlers/BicepHoverHandler.cs +++ b/src/Bicep.LangServer/Handlers/BicepHoverHandler.cs @@ -88,6 +88,15 @@ public BicepHoverHandler(ISymbolResolver symbolResolver) : base(CreateRegistrati // but this simplifies the checks return GetFunctionMarkdown(function, functionCall.Arguments, result.Origin, result.Context.Compilation.GetEntrypointSemanticModel()); + case PropertySymbol property: + var markdown = $"```bicep\n{property.Name}: {property.Type}\n```\n"; + if (property.Description is not null) + { + markdown += $"{property.Description}\n"; + } + + return markdown; + case FunctionSymbol function when result.Origin is InstanceFunctionCallSyntax functionCall: return GetFunctionMarkdown(function, functionCall.Arguments, result.Origin, result.Context.Compilation.GetEntrypointSemanticModel()); diff --git a/src/Bicep.LangServer/Handlers/BicepReferencesHandler.cs b/src/Bicep.LangServer/Handlers/BicepReferencesHandler.cs index 97af3f916c5..d44d8fb1102 100644 --- a/src/Bicep.LangServer/Handlers/BicepReferencesHandler.cs +++ b/src/Bicep.LangServer/Handlers/BicepReferencesHandler.cs @@ -4,6 +4,8 @@ using System.Threading; using System.Threading.Tasks; using Bicep.Core.Navigation; +using Bicep.Core.Semantics; +using Bicep.Core.Syntax; using Bicep.LanguageServer.Providers; using Bicep.LanguageServer.Utils; using OmniSharp.Extensions.LanguageServer.Protocol.Document; @@ -28,13 +30,19 @@ public override Task Handle(ReferenceParams request, Cancella return Task.FromResult(new LocationContainer()); } + if (result.Symbol is PropertySymbol) + { + // TODO: Implement for PropertySymbol + return Task.FromResult(new LocationContainer()); + } + var references = result.Context.Compilation.GetEntrypointSemanticModel() .FindReferences(result.Symbol) .Where(referenceSyntax => request.Context.IncludeDeclaration || !(referenceSyntax is INamedDeclarationSyntax)) .Select(referenceSyntax => new Location { Uri = request.TextDocument.Uri, - Range = PositionHelper.GetNameRange(result.Context.LineStarts, referenceSyntax) + Range = PositionHelper.GetNameRange(result.Context.LineStarts, referenceSyntax), }); return Task.FromResult(new LocationContainer(references)); diff --git a/src/Bicep.LangServer/Handlers/BicepRenameHandler.cs b/src/Bicep.LangServer/Handlers/BicepRenameHandler.cs index b7a203d0f3b..26bfbef0112 100644 --- a/src/Bicep.LangServer/Handlers/BicepRenameHandler.cs +++ b/src/Bicep.LangServer/Handlers/BicepRenameHandler.cs @@ -35,6 +35,12 @@ public BicepRenameHandler(ISymbolResolver symbolResolver) : base(CreateRegistrat return Task.FromResult(null); } + if (result.Symbol is PropertySymbol) + { + // TODO: Implement for PropertySymbol + return Task.FromResult(null); + } + var textEdits = result.Context.Compilation.GetEntrypointSemanticModel() .FindReferences(result.Symbol) .Select(GetIdentifier) @@ -64,6 +70,12 @@ public BicepRenameHandler(ISymbolResolver symbolResolver) : base(CreateRegistrat case INamedDeclarationSyntax declarationSyntax: return declarationSyntax.Name; + case PropertyAccessSyntax propertyAccessSyntax: + return propertyAccessSyntax.PropertyName; + + case ObjectPropertySyntax objectPropertySyntax: + return objectPropertySyntax.Key as IdentifierSyntax; + default: return null; } diff --git a/src/Bicep.LangServer/Providers/BicepSymbolResolver.cs b/src/Bicep.LangServer/Providers/BicepSymbolResolver.cs index aef2222fe1c..5abe31cb4be 100644 --- a/src/Bicep.LangServer/Providers/BicepSymbolResolver.cs +++ b/src/Bicep.LangServer/Providers/BicepSymbolResolver.cs @@ -4,6 +4,7 @@ using Bicep.Core.Parsing; using Bicep.Core.Semantics; using Bicep.Core.Syntax; +using Bicep.Core.TypeSystem; using Bicep.LanguageServer.CompilationManager; using Bicep.LanguageServer.Utils; using Microsoft.Extensions.Logging; @@ -36,10 +37,14 @@ public BicepSymbolResolver(ILogger logger, ICompilationMana // convert text coordinates int offset = PositionHelper.GetOffset(context.LineStarts, position); + var semanticModel = context.Compilation.GetEntrypointSemanticModel(); - // locate the most specific node that intersects with the text coordinate that isn't an identifier - SyntaxBase? node = context.ProgramSyntax.TryFindMostSpecificNodeInclusive(offset, n => !(n is IdentifierSyntax) && !(n is Token)); - if (node == null) + // locate the most specific node that can be bound as a symbol + var node = context.ProgramSyntax.TryFindMostSpecificNodeInclusive( + offset, + n => n is not IdentifierSyntax && n is not Token); + + if (node is null) { // the program node must enclose all locations in the file, so this should not happen this.logger.LogError("The symbol resolution request position exceeded the bounds of the file '{Uri}'.", uri); @@ -47,15 +52,12 @@ public BicepSymbolResolver(ILogger logger, ICompilationMana return null; } - // resolve symbol (if any) - Symbol? symbol = context.Compilation.GetEntrypointSemanticModel().GetSymbolInfo(node); - if (symbol == null) + if (semanticModel.GetSymbolInfo(node) is {} symbol) { - // not a symbol - return null; + return new SymbolResolutionResult(node, symbol, context); } - return new SymbolResolutionResult(node, symbol, context); + return null; } } -} +} \ No newline at end of file diff --git a/src/Bicep.LangServer/Utils/PositionHelper.cs b/src/Bicep.LangServer/Utils/PositionHelper.cs index 175f4bffbe6..f397ef4ddf4 100644 --- a/src/Bicep.LangServer/Utils/PositionHelper.cs +++ b/src/Bicep.LangServer/Utils/PositionHelper.cs @@ -20,20 +20,17 @@ public static Position GetPosition(ImmutableArray lineStarts, in int offset public static int GetOffset(ImmutableArray lineStarts, Position position) => TextCoordinateConverter.GetOffset(lineStarts, position.Line, position.Character); public static Range GetNameRange(ImmutableArray lineStarts, SyntaxBase syntax) - { - if (syntax is INamedDeclarationSyntax declarationSyntax) - { - return declarationSyntax.Name.ToRange(lineStarts); - } - - if (syntax is ISymbolReference symbolRef) - { - return symbolRef.Name.ToRange(lineStarts); - } + => GetNameSyntax(syntax).ToRange(lineStarts); - // it's something else - fallback to syntax node's span - return syntax.ToRange(lineStarts); - } + public static SyntaxBase GetNameSyntax(SyntaxBase syntax) + => syntax switch { + INamedDeclarationSyntax x => x.Name, + ISymbolReference x => x.Name, + PropertyAccessSyntax x => x.PropertyName, + ObjectPropertySyntax x => x.Key, + // it's something else - fallback to syntax node's span + _ => syntax, + }; } }