Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Scaffold required resource snippet based on the resourceType & apiVersion #2172

Merged
merged 13 commits into from
May 5, 2021

Conversation

bhsubra
Copy link
Contributor

@bhsubra bhsubra commented Apr 8, 2021

Fixes #804

Provides resource body snippet completion for static templates and swagger

BicepIssue804

@bhsubra bhsubra self-assigned this Apr 8, 2021
@bhsubra bhsubra requested a review from majastrz April 8, 2021 02:00
using Bicep.LanguageServer.Completions;

namespace Bicep.LanguageServer.Snippets
{
public class SnippetsProvider : ISnippetsProvider
{
private Dictionary<string, string> resourceTypeToBodyMap = new Dictionary<string, string>();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resourceTypeToBodyMap [](start = 43, length = 21)

Is it possible for the dictionaries to be modified concurrently?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And could you add comments to these to explain what they are storing and how that's used?


In reply to: 610234912 [](ancestors = 610234912)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added comments here:
9b3995e

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the concurrency question? Can multiple threads be modifying the dictionary at the same time?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I missed this. I don't see a problem with resourceTypeToBodyMap and resourceTypeToDependentsMap as they are modified during Initialize(..). Should happen only once. I am not entirely sure of resourceBodySnippetsCache. I updated to use ConcurrentDictionary to be safe. Let me know if you see any issues or think we need locking mechanism instead.

4fc7f62

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good.

Copy link
Member

@majastrz majastrz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some comments and questions.

@alex-frankel
Copy link
Collaborator

How do I trigger this? I tried the vsix attached to this build and couldn't figure it out.

@MarcusFelling
Copy link
Collaborator

I would like to discuss taking this a step further in our next DSL discussion. This PR has the snippet sourced from a static snippet template. How do we make this more dynamic so snippets don't require updating over time with new api-versions and additional resource types? Can the required properties be pulled from the swagger spec to populate this scaffolding?

@majastrz
Copy link
Member

I would like to discuss taking this a step further in our next DSL discussion. This PR has the snippet sourced from a static snippet template. How do we make this more dynamic so snippets don't require updating over time with new api-versions and additional resource types? Can the required properties be pulled from the swagger spec to populate this scaffolding?

100% agree. Even though our swagger info is incomplete when it comes to required properties, it will be a great improvement regardless. (And it will also generate issues that should help us drive improvements to required properties in swaggers.)

@anthony-c-martin
Copy link
Member

I would like to discuss taking this a step further in our next DSL discussion. This PR has the snippet sourced from a static snippet template. How do we make this more dynamic so snippets don't require updating over time with new api-versions and additional resource types? Can the required properties be pulled from the swagger spec to populate this scaffolding?

My understanding is that was what #804 was created to address. I'm a bit confused as this PR doesn't seem to be addressing that feature requrest.

@bhsubra would you mind adding a description of what this PR is fixing?

@bhsubra
Copy link
Contributor Author

bhsubra commented Apr 12, 2021

I would like to discuss taking this a step further in our next DSL discussion. This PR has the snippet sourced from a static snippet template. How do we make this more dynamic so snippets don't require updating over time with new api-versions and additional resource types? Can the required properties be pulled from the swagger spec to populate this scaffolding?

My understanding is that was what #804 was created to address. I'm a bit confused as this PR doesn't seem to be addressing that feature requrest.

@bhsubra would you mind adding a description of what this PR is fixing?

@anthony-c-martin , updated the description. Could you please take a look and let me know what I am missing? Thanks!

@bhsubra
Copy link
Contributor Author

bhsubra commented Apr 12, 2021

How do I trigger this? I tried the vsix attached to this build and couldn't figure it out.

@alex-frankel , updated description and attached an example gif. Could you please take a look? Thanks!

@anthony-c-martin
Copy link
Member

anthony-c-martin commented Apr 13, 2021

@anthony-c-martin , updated the description. Could you please take a look and let me know what I am missing? Thanks!

My understanding is that the feature request is to generate snippets at runtime, purely based off type information (not from user-submitted files).

To give a concrete example (cursor is |), imagine the user has typed the following and requests completions:
resource storage 'Microsoft.Storage/storageAccounts@2021-02-01' = |

We don't have any user-submitted snippets available for this type, so the best we can offer is {} as the completion (empty object).

However, once I've accepted the completion of {}, you can see we get an immediate type error that name, kind, sku and location are required but not supplied:
image

So in this case, because we know from the type system that these required fields must be supplied, we could instead dynamically generate a snippet that looks like:

{
  name: $1
  kind: $2
  sku: $3
  location: $4
  $0
}

The same applies at different levels of nesting - e.g. if you've got something like:

resource storage 'Microsoft.Storage/storageAccounts@2021-02-01' = {
  name: 'asdf'
  kind: 'BlobStorage'
  location: 'West US'
  sku: |
}

Again, the type system knows that name is a required field for the sku object, so instead of just {}, we could offer:

{
  name: $1
  $0
}

@MarcusFelling
Copy link
Collaborator

@anthony-c-martin bingo. I think this PR can be merged as it's a step in the right direction and a new PR can be created for the snippet generation functionality?

@anthony-c-martin
Copy link
Member

@anthony-c-martin bingo. I think this PR can be merged as it's a step in the right direction and a new PR can be created for the snippet generation functionality?

Yup, totally

@bhsubra bhsubra force-pushed the dev/bhsubra/FixForSnippetsIssue804 branch 2 times, most recently from e97046a to 8ce87e0 Compare April 18, 2021 05:14
@majastrz
Copy link
Member

When I inserted the KV required properties I got this:
image

Can you take a look? We have some multi-line snippets that are doing the right things when it comes to indentation and such.

return GetResourceBodyCompletionSnippetFromSwaggerSpec(typeSymbol, label);
}

private Snippet GetResourceBodyCompletionSnippetFromSwaggerSpec(TypeSymbol typeSymbol, string label)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] GetResourceBodyCompletionSnippetFromAzTypes ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated here:
4fc7f62


if (typeProperty.TypeReference.Type is ObjectType objectType)
{
snippetText += "\t" + typeProperty.Name + ": {\n";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"\t"

I think you will need to keep track of the indent level to help VS code format the nested properties correctly via AdjustIndentation

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is related to one of the screenshots I posted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed formatting here:
c1e6263


foreach (KeyValuePair<string, TypeProperty> kvp in objectType.Properties.OrderBy(x => x.Key))
{
snippetText = GetSnippetText(kvp.Value, snippetText, ref index);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be +=?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Refactored the code here to use StringBuilder instead:
4fc7f62


if (typeProperty.TypeReference.Type is ObjectType objectType)
{
snippetText += "\t" + typeProperty.Name + ": {\n";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+=

Nit - could you switch to a StringBuilder to construct these? Each concatenation allocates more strings. On the other hand, they would be very short-lived and not live past gen0, so there probably isn't a huge memory impact anyway.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In #2341, I believe @jfleisher has done the work to construct a well-formatted partial document from Bicep syntax nodes. I wonder if it's worth using what he's done - that way there would be no need to manually format the document via text manipulation (which I'm sure has a bunch of edge cases).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed formatting here:
c1e6263

Switched to use StringBuilder here:
4fc7f62

@majastrz
Copy link
Member

Do we have any canonical ordering that we should follow when generating the required property snippets? For example should the name property be first? (There might be some ARM code we could borrow that could help here as well.)

@majastrz
Copy link
Member

From our last team discussion, didn't we want separate completions for {} and "insert object with required properties"?
image

@alex-frankel?

@bhsubra bhsubra force-pushed the dev/bhsubra/FixForSnippetsIssue804 branch from c9ad548 to 4509dd1 Compare April 26, 2021 15:02
@alex-frankel
Copy link
Collaborator

Do we have any canonical ordering that we should follow when generating the required property snippets?

Not everything is applicable, but we should follow the guidance from the quick start contribution guide: https://github.com/Azure/azure-quickstart-templates/blob/master/1-CONTRIBUTION-GUIDE/best-practices.md#resources

@alex-frankel
Copy link
Collaborator

didn't we want separate completions for {} and "insert object with required properties"?

Yes - I think empty object ({}) should insert just the empty object and a newline. There should be a separate item for scaffolding. I think we might have said insert-required?

@bhsubra bhsubra force-pushed the dev/bhsubra/FixForSnippetsIssue804 branch from 721c61c to 43a0fe1 Compare May 5, 2021 00:06
@majastrz
Copy link
Member

majastrz commented May 5, 2021

There is an issue with resource types whose bodies are discriminated object types:
image

This will require additional discussion on how we fix it, so I'm fine if the fix comes with a different PR.

@majastrz
Copy link
Member

majastrz commented May 5, 2021

I have an idea how we can approach it, but we should discuss with the rest of the team tomorrow.

Copy link
Member

@majastrz majastrz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is really cool! Thanks for getting this done!

@bhsubra bhsubra merged commit dab7a34 into main May 5, 2021
@bhsubra bhsubra deleted the dev/bhsubra/FixForSnippetsIssue804 branch May 5, 2021 02:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Scaffold required resource snippet based on the resourceType & apiVersion
5 participants