Description
Background
As part of #869 we introduced completion for registry module sources. The first version of the issue included resolving module inputs as well. After attempting this and running into a couple of nontrivial problems, we created this follow-up issue.
When a user requests auto-completion inside a module source, we send a search request to Algolia, returning all the matching modules. Furthermore, on selecting a completion item, VS Code will send a resolve request to the LS, which we intend to use for enriching the item with the module inputs fetched from the TF registry.
Expected User Experience
Fill the required module inputs when completing the module source:
Provide a similar experience as for resources:
This includes:
- Using snippets with placeholders for tabbing through all inputs after insertion
- Showing a preview if a user has
editor.suggest.preview
enabled
Challenges / Findings
- While it's possible to set the
insertTextFormat
for thetextEdit
of aCompletionItem
, it's ignored foradditionalTextEdits
. Without it, VS Code won't render any tabstops/placeholders. - A
completionItem/resolve
request can only update the following three properties:documentation
,detail
,additionalTextEdits
- Without being able to modify
textEdit
when resolving an item, all information must be part of the initialtextDocument/completion
request - Getting the module inputs requires a request to the registry. This would lead to a lot of overhead before we can even show any suggestions and 9/10 module inputs would be discarded afterwards, since the user picks only one item
- The
editor.suggest.preview
feature only shows modifications done by thetextEdit
and doesn't show any modifications done byadditionalTextEdits
Proposal
hashicorp/hcl-lang
To be able to build the additionalTextEdits
, we need more context inside the resolve hook:
- Extend
UnresolvedCandidate
- with the candidate label / insert text
- the position (we can get this from the candidate's
textEdit
)
hashicorp/terraform-ls
- Add a
ResolveRegistryModuleSource
hook
func (h *Hooks) ResolveRegistryModuleSource(ctx context.Context, unresolvedCandidate decoder.UnresolvedCandidate) (*decoder.ResolvedCandidate, error) {
candidate := decoder.ResolvedCandidate{}
source, err := strconv.Unquote(unresolvedCandidate.Label) // or use unresolvedCandidate.RawInsertText
if err != nil {
return &candidate, err
}
addr, err := tfaddr.ParseModuleSource(source)
if err != nil {
return &candidate, err
}
modMeta, err := h.RegistryClient.GetModuleData(ctx, addr, nil)
if err != nil {
return &candidate, err
}
inputs := convertInputs(modMeta.Root.Inputs)
// TODO: generate snippet from inputs
candidate.AdditionalTextEdits = ...
return &candidate, nil
}
- Extend
CompletionItemResolve
handler to add more context to theUnresolvedCandidate