diff --git a/go.mod b/go.mod index 6a8a7dbb2..263caa8b3 100644 --- a/go.mod +++ b/go.mod @@ -11,12 +11,12 @@ require ( github.com/hashicorp/go-memdb v1.3.2 github.com/hashicorp/go-multierror v1.1.1 github.com/hashicorp/go-version v1.3.0 - github.com/hashicorp/hcl-lang v0.0.0-20210419185146-8556dd730bc7 + github.com/hashicorp/hcl-lang v0.0.0-20210514214009-15d3afdead68 github.com/hashicorp/hcl/v2 v2.10.0 github.com/hashicorp/terraform-exec v0.13.3 github.com/hashicorp/terraform-json v0.11.0 github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896 - github.com/hashicorp/terraform-schema v0.0.0-20210428174709-ad0461b43827 + github.com/hashicorp/terraform-schema v0.0.0-20210514214427-b947185f165d github.com/mh-cbon/go-fmt-fail v0.0.0-20160815164508-67765b3fbcb5 github.com/mitchellh/cli v1.1.2 github.com/mitchellh/copystructure v1.2.0 diff --git a/go.sum b/go.sum index 3cfe9adb1..accc6c63c 100644 --- a/go.sum +++ b/go.sum @@ -188,10 +188,9 @@ github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uG github.com/hashicorp/hcl v0.0.0-20170504190234-a4b07c25de5f/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/hcl-lang v0.0.0-20210419185146-8556dd730bc7 h1:6ajS9kFs/7UnRcy3dHThUnmicqU7VjGUn0RLOT6Ewq0= -github.com/hashicorp/hcl-lang v0.0.0-20210419185146-8556dd730bc7/go.mod h1:VRVfqufUmJSpWsoWDtYV/BejqCV+NNyS9V9vR0dcivs= +github.com/hashicorp/hcl-lang v0.0.0-20210514214009-15d3afdead68 h1:Y5HxVHg9bKiS8Q4Q3dHOdETtF4DEmKNVdBh93o5K1L0= +github.com/hashicorp/hcl-lang v0.0.0-20210514214009-15d3afdead68/go.mod h1:7BtIzYAy75UR501SFkNjL98xMZbvn5Vc7bj+dOgcH70= github.com/hashicorp/hcl/v2 v2.0.0/go.mod h1:oVVDG71tEinNGYCxinCYadcmKU9bglqW9pV3txagJ90= -github.com/hashicorp/hcl/v2 v2.9.1/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg= github.com/hashicorp/hcl/v2 v2.10.0 h1:1S1UnuhDGlv3gRFV4+0EdwB+znNP5HmcGbIqwnSCByg= github.com/hashicorp/hcl/v2 v2.10.0/go.mod h1:FwWsfWEjyV/CMj8s/gqAuiviY72rJ1/oayI9WftqcKg= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= @@ -207,8 +206,8 @@ github.com/hashicorp/terraform-json v0.11.0 h1:4zDqqW2F3kOysORIaYKFGgWDYIRA3hwqx github.com/hashicorp/terraform-json v0.11.0/go.mod h1:pmbq9o4EuL43db5+0ogX10Yofv1nozM+wskr/bGFJpI= github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896 h1:1FGtlkJw87UsTMg5s8jrekrHmUPUJaMcu6ELiVhQrNw= github.com/hashicorp/terraform-registry-address v0.0.0-20210412075316-9b2996cce896/go.mod h1:bzBPnUIkI0RxauU8Dqo+2KrZZ28Cf48s8V6IHt3p4co= -github.com/hashicorp/terraform-schema v0.0.0-20210428174709-ad0461b43827 h1:wf2hrzKEi5WOZkp5U0GGLKsslbFrGob2GJ35rjwMiO0= -github.com/hashicorp/terraform-schema v0.0.0-20210428174709-ad0461b43827/go.mod h1:K3XjJOEolIgMNwNb/cVbSSO8nBxvO/JLDAuTOLbT7XQ= +github.com/hashicorp/terraform-schema v0.0.0-20210514214427-b947185f165d h1:XFxb7PJ76yRdoo/IINuh2ASgujNL8jlhaeulXVbDVUA= +github.com/hashicorp/terraform-schema v0.0.0-20210514214427-b947185f165d/go.mod h1:qzSjebjYVlcz56pBd2It3XmkOywXHNjnC/420WHSdaU= github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734 h1:HKLsbzeOsfXmKNpr3GiT18XAblV0BjCbzL8KQAMZGa0= github.com/hashicorp/terraform-svchost v0.0.0-20200729002733-f050f53b9734/go.mod h1:kNDNcF7sN4DocDLBkQYz73HGKwN1ANB1blq4lIYLYvg= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= @@ -266,7 +265,6 @@ github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceT github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw= github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= -github.com/mitchellh/copystructure v1.1.2/go.mod h1:EBArHfARyrSWO/+Wyr9zwEkc6XMFB9XyNgFNmRkZZU4= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= @@ -284,7 +282,6 @@ github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -383,7 +380,6 @@ github.com/zclconf/go-cty v1.1.0/go.mod h1:xnAOWiHeOqg2nWS62VtQ7pbOu17FtxJNW8RLE github.com/zclconf/go-cty v1.2.0/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= github.com/zclconf/go-cty v1.2.1/go.mod h1:hOPWgoHbaTUnI5k4D2ld+GRpFJSCe6bCM7m1q/N4PQ8= github.com/zclconf/go-cty v1.8.0/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= -github.com/zclconf/go-cty v1.8.1/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= github.com/zclconf/go-cty v1.8.2/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= github.com/zclconf/go-cty v1.8.3 h1:48gwZXrdSADU2UW9eZKHprxAI7APZGW9XmExpJpSjT0= github.com/zclconf/go-cty v1.8.3/go.mod h1:vVKLxnk3puL4qRAv72AO+W99LUD4da90g3uUAzyuvAk= diff --git a/internal/decoder/decoder.go b/internal/decoder/decoder.go index 46438bf89..533fb923f 100644 --- a/internal/decoder/decoder.go +++ b/internal/decoder/decoder.go @@ -5,12 +5,18 @@ import ( "fmt" "github.com/hashicorp/hcl-lang/decoder" + "github.com/hashicorp/hcl-lang/lang" lsctx "github.com/hashicorp/terraform-ls/internal/context" "github.com/hashicorp/terraform-ls/internal/terraform/module" ) func DecoderForModule(ctx context.Context, mod module.Module) (*decoder.Decoder, error) { d := decoder.NewDecoder() + + d.SetReferenceReader(func() lang.References { + return mod.References + }) + d.SetUtmSource("terraform-ls") d.UseUtmContent(true) diff --git a/internal/langserver/handlers/complete_test.go b/internal/langserver/handlers/complete_test.go index dd360d951..eeb67f796 100644 --- a/internal/langserver/handlers/complete_test.go +++ b/internal/langserver/handlers/complete_test.go @@ -44,7 +44,7 @@ func TestCompletion_withValidData(t *testing.T) { ls := langserver.NewLangServerMock(t, NewMockSession(&MockSessionInput{ TerraformCalls: &exec.TerraformMockCalls{ PerWorkDir: map[string][]*mock.Call{ - tmpDir.Dir(): []*mock.Call{ + tmpDir.Dir(): { { Method: "Version", Repeatability: 1, @@ -224,6 +224,219 @@ func TestCompletion_withValidData(t *testing.T) { }`) } +func TestCompletion_withValidDataAndSnippets(t *testing.T) { + tmpDir := TempDir(t) + InitPluginCache(t, tmpDir.Dir()) + + var testSchema tfjson.ProviderSchemas + err := json.Unmarshal([]byte(testSchemaOutput), &testSchema) + if err != nil { + t.Fatal(err) + } + + ls := langserver.NewLangServerMock(t, NewMockSession(&MockSessionInput{ + TerraformCalls: &exec.TerraformMockCalls{ + PerWorkDir: map[string][]*mock.Call{ + tmpDir.Dir(): { + { + Method: "Version", + Repeatability: 1, + Arguments: []interface{}{ + mock.AnythingOfType(""), + }, + ReturnArguments: []interface{}{ + version.Must(version.NewVersion("0.12.0")), + nil, + nil, + }, + }, + { + Method: "GetExecPath", + Repeatability: 1, + ReturnArguments: []interface{}{ + "", + }, + }, + { + Method: "ProviderSchemas", + Repeatability: 1, + Arguments: []interface{}{ + mock.AnythingOfType(""), + }, + ReturnArguments: []interface{}{ + &testSchema, + nil, + }, + }, + }, + }, + }})) + stop := ls.Start(t) + defer stop() + + ls.Call(t, &langserver.CallRequest{ + Method: "initialize", + ReqParams: fmt.Sprintf(`{ + "capabilities": { + "textDocument": { + "completion": { + "completionItem": { + "snippetSupport": true + } + } + } + }, + "rootUri": %q, + "processId": 12345 + }`, tmpDir.URI())}) + ls.Notify(t, &langserver.CallRequest{ + Method: "initialized", + ReqParams: "{}", + }) + ls.Call(t, &langserver.CallRequest{ + Method: "textDocument/didOpen", + ReqParams: fmt.Sprintf(`{ + "textDocument": { + "version": 0, + "languageId": "terraform", + "text": "provider \"test\" {\n\n}\n", + "uri": "%s/main.tf" + } + }`, tmpDir.URI())}) + + ls.CallAndExpectResponse(t, &langserver.CallRequest{ + Method: "textDocument/completion", + ReqParams: fmt.Sprintf(`{ + "textDocument": { + "uri": "%s/main.tf" + }, + "position": { + "character": 0, + "line": 1 + } + }`, tmpDir.URI())}, `{ + "jsonrpc": "2.0", + "id": 3, + "result": { + "isIncomplete": false, + "items": [ + { + "label": "alias", + "kind": 10, + "detail": "Optional, string", + "documentation": "Alias for using the same provider with different configurations for different resources, e.g. eu-west", + "insertTextFormat": 2, + "textEdit": { + "range": { + "start": { + "line": 1, + "character": 0 + }, + "end": { + "line": 1, + "character": 0 + } + }, + "newText": "alias = \"${1:value}\"" + } + }, + { + "label": "anonymous", + "kind": 10, + "detail": "Optional, number", + "documentation": "Desc 1", + "insertTextFormat": 2, + "textEdit": { + "range": { + "start": { + "line": 1, + "character": 0 + }, + "end": { + "line": 1, + "character": 0 + } + }, + "newText": "anonymous = " + }, + "command": { + "title": "Suggest", + "command": "editor.action.triggerSuggest" + } + }, + { + "label": "base_url", + "kind": 10, + "detail": "Optional, string", + "documentation": "Desc 2", + "insertTextFormat": 2, + "textEdit": { + "range": { + "start": { + "line": 1, + "character": 0 + }, + "end": { + "line": 1, + "character": 0 + } + }, + "newText": "base_url = " + }, + "command": { + "title": "Suggest", + "command": "editor.action.triggerSuggest" + } + }, + { + "label": "individual", + "kind": 10, + "detail": "Optional, bool", + "documentation": "Desc 3", + "insertTextFormat": 2, + "textEdit": { + "range": { + "start": { + "line": 1, + "character": 0 + }, + "end": { + "line": 1, + "character": 0 + } + }, + "newText": "individual = " + }, + "command": { + "title": "Suggest", + "command": "editor.action.triggerSuggest" + } + }, + { + "label": "version", + "kind": 10, + "detail": "Optional, string", + "documentation": "Specifies a version constraint for the provider, e.g. ~\u003e 1.0", + "insertTextFormat": 2, + "textEdit": { + "range": { + "start": { + "line": 1, + "character": 0 + }, + "end": { + "line": 1, + "character": 0 + } + }, + "newText": "version = \"${1:value}\"" + } + } + ] + } + }`) +} + var testSchemaOutput = `{ "format_version": "0.1", "provider_schemas": { diff --git a/internal/langserver/handlers/did_change.go b/internal/langserver/handlers/did_change.go index fb748d30f..44d7dedfd 100644 --- a/internal/langserver/handlers/did_change.go +++ b/internal/langserver/handlers/did_change.go @@ -67,6 +67,10 @@ func TextDocumentDidChange(ctx context.Context, params lsp.DidChangeTextDocument if err != nil { return err } + err = modMgr.EnqueueModuleOpWait(mod.Path, op.OpTypeDecodeReferences) + if err != nil { + return err + } diags, err := lsctx.Diagnostics(ctx) if err != nil { diff --git a/internal/langserver/handlers/did_open.go b/internal/langserver/handlers/did_open.go index 525b410a7..33688dd30 100644 --- a/internal/langserver/handlers/did_open.go +++ b/internal/langserver/handlers/did_open.go @@ -48,6 +48,7 @@ func (lh *logHandler) TextDocumentDidOpen(ctx context.Context, params lsp.DidOpe // TODO: Do this only if we can verify the file differs? modMgr.EnqueueModuleOpWait(mod.Path, op.OpTypeParseConfiguration) modMgr.EnqueueModuleOpWait(mod.Path, op.OpTypeLoadModuleMetadata) + modMgr.EnqueueModuleOpWait(mod.Path, op.OpTypeDecodeReferences) if mod.TerraformVersionState == op.OpStateUnknown { modMgr.EnqueueModuleOp(mod.Path, op.OpTypeGetTerraformVersion) diff --git a/internal/lsp/completion.go b/internal/lsp/completion.go index 89422e08f..6178c1627 100644 --- a/internal/lsp/completion.go +++ b/internal/lsp/completion.go @@ -48,12 +48,14 @@ func toCompletionItem(candidate lang.Candidate, caps lsp.CompletionClientCapabil kind = lsp.EnumCompletion case lang.MapCandidateKind, lang.ObjectCandidateKind: kind = lsp.StructCompletion + case lang.TraversalCandidateKind: + kind = lsp.VariableCompletion } // TODO: Omit item which uses kind unsupported by the client var cmd *lsp.Command - if candidate.TriggerSuggest { + if candidate.TriggerSuggest && snippetSupport { cmd = &lsp.Command{ Command: "editor.action.triggerSuggest", Title: "Suggest", diff --git a/internal/lsp/token_encoder.go b/internal/lsp/token_encoder.go index e8a4cb470..2a4d1b454 100644 --- a/internal/lsp/token_encoder.go +++ b/internal/lsp/token_encoder.go @@ -47,6 +47,10 @@ func (te *TokenEncoder) encodeTokenOfIndex(i int) []float64 { tokenType = TokenTypeParameter case lang.TokenMapKey: tokenType = TokenTypeParameter + case lang.TokenKeyword: + tokenType = TokenTypeVariable + case lang.TokenTraversalStep: + tokenType = TokenTypeVariable default: return []float64{} diff --git a/internal/lsp/token_types.go b/internal/lsp/token_types.go index ca7d19cd1..cfb8d4ace 100644 --- a/internal/lsp/token_types.go +++ b/internal/lsp/token_types.go @@ -108,6 +108,7 @@ var ( TokenTypeKeyword, TokenTypeNumber, TokenTypeParameter, + TokenTypeVariable, } serverTokenModifiers = TokenModifiers{ TokenModifierDeprecated, diff --git a/internal/state/module.go b/internal/state/module.go index 48132084f..2cbabe43e 100644 --- a/internal/state/module.go +++ b/internal/state/module.go @@ -3,6 +3,7 @@ package state import ( "github.com/hashicorp/go-memdb" "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/lang" "github.com/hashicorp/hcl/v2" tfaddr "github.com/hashicorp/terraform-registry-address" tfmod "github.com/hashicorp/terraform-schema/module" @@ -32,6 +33,10 @@ type Module struct { ProviderSchemaErr error ProviderSchemaState op.OpState + References lang.References + ReferencesErr error + ReferencesState op.OpState + ParsedFiles map[string]*hcl.File ParsingErr error ParsingState op.OpState @@ -49,6 +54,7 @@ func newModule(modPath string) *Module { ModManifestState: op.OpStateUnknown, TerraformVersionState: op.OpStateUnknown, ProviderSchemaState: op.OpStateUnknown, + ReferencesState: op.OpStateUnknown, ParsingState: op.OpStateUnknown, MetaState: op.OpStateUnknown, } @@ -400,3 +406,46 @@ func (s *ModuleStore) UpdateDiagnostics(path string, diags map[string]hcl.Diagno txn.Commit() return nil } + +func (s *ModuleStore) SetReferencesState(path string, state op.OpState) error { + txn := s.db.Txn(true) + defer txn.Abort() + + mod, err := moduleByPath(txn, path) + if err != nil { + return err + } + + mod.ReferencesState = state + err = txn.Insert(s.tableName, mod) + if err != nil { + return err + } + + txn.Commit() + return nil +} + +func (s *ModuleStore) UpdateReferences(path string, refs lang.References, rErr error) error { + txn := s.db.Txn(true) + txn.Defer(func() { + s.SetReferencesState(path, op.OpStateLoaded) + }) + defer txn.Abort() + + mod, err := moduleByPath(txn, path) + if err != nil { + return err + } + + mod.References = refs + mod.ReferencesErr = rErr + + err = txn.Insert(s.tableName, mod) + if err != nil { + return err + } + + txn.Commit() + return nil +} diff --git a/internal/terraform/module/builtin_references.go b/internal/terraform/module/builtin_references.go new file mode 100644 index 000000000..df9fdf41b --- /dev/null +++ b/internal/terraform/module/builtin_references.go @@ -0,0 +1,53 @@ +package module + +import ( + "github.com/hashicorp/hcl-lang/lang" + "github.com/zclconf/go-cty/cty" +) + +var builtinScopeId = lang.ScopeId("builtin") + +func builtinReferences(modPath string) lang.References { + return lang.References{ + { + Addr: lang.Address{ + lang.RootStep{Name: "path"}, + lang.AttrStep{Name: "module"}, + }, + ScopeId: builtinScopeId, + Type: cty.String, + Description: lang.Markdown("The filesystem path of the module where the expression is placed\n\n" + + modPath), + }, + { + Addr: lang.Address{ + lang.RootStep{Name: "path"}, + lang.AttrStep{Name: "root"}, + }, + ScopeId: builtinScopeId, + Type: cty.String, + Description: lang.Markdown("The filesystem path of the root module of the configuration"), + }, + { + Addr: lang.Address{ + lang.RootStep{Name: "path"}, + lang.AttrStep{Name: "cwd"}, + }, + ScopeId: builtinScopeId, + Type: cty.String, + Description: lang.Markdown("The filesystem path of the current working directory.\n\n" + + "In normal use of Terraform this is the same as `path.root`, " + + "but some advanced uses of Terraform run it from a directory " + + "other than the root module directory, causing these paths to be different."), + }, + { + Addr: lang.Address{ + lang.RootStep{Name: "terraform"}, + lang.AttrStep{Name: "workspace"}, + }, + ScopeId: builtinScopeId, + Type: cty.String, + Description: lang.Markdown("The name of the currently selected workspace"), + }, + } +} diff --git a/internal/terraform/module/module_loader.go b/internal/terraform/module/module_loader.go index 76fbfbf40..6f990cfa8 100644 --- a/internal/terraform/module/module_loader.go +++ b/internal/terraform/module/module_loader.go @@ -181,6 +181,12 @@ func (ml *moduleLoader) executeModuleOp(ctx context.Context, modOp ModuleOperati ml.logger.Printf("failed to load module metadata: %s", err) } return + case op.OpTypeDecodeReferences: + err := DecodeReferences(ml.modStore, ml.schemaStore, modOp.ModulePath) + if err != nil { + ml.logger.Printf("failed to decode references: %s", err) + } + return } ml.logger.Printf("%s: unknown operation (%#v) for module operation", @@ -226,6 +232,12 @@ func (ml *moduleLoader) EnqueueModuleOp(modOp ModuleOperation) error { return nil } ml.modStore.SetMetaState(modOp.ModulePath, op.OpStateQueued) + case op.OpTypeDecodeReferences: + if mod.MetaState == op.OpStateQueued { + // avoid enqueuing duplicate operation + return nil + } + ml.modStore.SetReferencesState(modOp.ModulePath, op.OpStateQueued) } ml.queue.PushOp(modOp) diff --git a/internal/terraform/module/module_manager.go b/internal/terraform/module/module_manager.go index 475843230..8b5e604ca 100644 --- a/internal/terraform/module/module_manager.go +++ b/internal/terraform/module/module_manager.go @@ -103,6 +103,10 @@ func (mm *moduleManager) SchemaForModule(modPath string) (*schema.BodySchema, er return nil, err } + return schemaForModule(mod, mm.schemaStore) +} + +func schemaForModule(mod *state.Module, schemaReader state.SchemaReader) (*schema.BodySchema, error) { var coreSchema *schema.BodySchema coreRequirements := make(version.Constraints, 0) if mod.TerraformVersion != nil { @@ -120,7 +124,7 @@ func (mm *moduleManager) SchemaForModule(modPath string) (*schema.BodySchema, er } sm := tfschema.NewSchemaMerger(coreSchema) - sm.SetSchemaReader(mm.schemaStore) + sm.SetSchemaReader(schemaReader) meta := &tfmodule.Meta{ Path: mod.Path, diff --git a/internal/terraform/module/module_ops.go b/internal/terraform/module/module_ops.go index 0b2995008..23ee29847 100644 --- a/internal/terraform/module/module_ops.go +++ b/internal/terraform/module/module_ops.go @@ -7,6 +7,8 @@ import ( "strings" "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/decoder" + "github.com/hashicorp/hcl-lang/lang" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/hcl/v2/hclsyntax" "github.com/hashicorp/terraform-ls/internal/filesystem" @@ -276,3 +278,45 @@ func LoadModuleMetadata(modStore *state.ModuleStore, modPath string) error { } return mErr } + +func DecodeReferences(modStore *state.ModuleStore, schemaReader state.SchemaReader, modPath string) error { + err := modStore.SetReferencesState(modPath, op.OpStateLoading) + if err != nil { + return err + } + + mod, err := modStore.ModuleByPath(modPath) + if err != nil { + return err + } + + d := decoder.NewDecoder() + for name, f := range mod.ParsedFiles { + err := d.LoadFile(name, f) + if err != nil { + return fmt.Errorf("failed to load a file: %w", err) + } + } + + fullSchema, schemaErr := schemaForModule(mod, schemaReader) + if schemaErr != nil { + sErr := modStore.UpdateReferences(modPath, lang.References{}, schemaErr) + if sErr != nil { + return sErr + } + return schemaErr + } + d.SetSchema(fullSchema) + + refs, rErr := d.DecodeReferences() + + bRefs := builtinReferences(modPath) + refs = append(refs, bRefs...) + + sErr := modStore.UpdateReferences(modPath, refs, rErr) + if sErr != nil { + return sErr + } + + return rErr +} diff --git a/internal/terraform/module/operation/op_type_string.go b/internal/terraform/module/operation/op_type_string.go index ce8cd2dc0..e4a322288 100644 --- a/internal/terraform/module/operation/op_type_string.go +++ b/internal/terraform/module/operation/op_type_string.go @@ -14,11 +14,12 @@ func _() { _ = x[OpTypeParseConfiguration-3] _ = x[OpTypeParseModuleManifest-4] _ = x[OpTypeLoadModuleMetadata-5] + _ = x[OpTypeDecodeReferences-6] } -const _OpType_name = "OpTypeUnknownOpTypeGetTerraformVersionOpTypeObtainSchemaOpTypeParseConfigurationOpTypeParseModuleManifestOpTypeLoadModuleMetadata" +const _OpType_name = "OpTypeUnknownOpTypeGetTerraformVersionOpTypeObtainSchemaOpTypeParseConfigurationOpTypeParseModuleManifestOpTypeLoadModuleMetadataOpTypeDecodeReferences" -var _OpType_index = [...]uint8{0, 13, 38, 56, 80, 105, 129} +var _OpType_index = [...]uint8{0, 13, 38, 56, 80, 105, 129, 151} func (i OpType) String() string { if i >= OpType(len(_OpType_index)-1) { diff --git a/internal/terraform/module/operation/operation.go b/internal/terraform/module/operation/operation.go index 64d263eb4..bbe3ea6d1 100644 --- a/internal/terraform/module/operation/operation.go +++ b/internal/terraform/module/operation/operation.go @@ -20,4 +20,5 @@ const ( OpTypeParseConfiguration OpTypeParseModuleManifest OpTypeLoadModuleMetadata + OpTypeDecodeReferences )