diff --git a/go.mod b/go.mod index 3f7c8abe..f63e5f0b 100644 --- a/go.mod +++ b/go.mod @@ -14,12 +14,12 @@ require ( github.com/hashicorp/go-uuid v1.0.3 github.com/hashicorp/go-version v1.7.0 github.com/hashicorp/hc-install v0.9.0 - github.com/hashicorp/hcl-lang v0.0.0-20241115124434-9d252ff73a68 + github.com/hashicorp/hcl-lang v0.0.0-20241206161211-32066ec5d37c github.com/hashicorp/hcl/v2 v2.23.0 github.com/hashicorp/terraform-exec v0.21.0 github.com/hashicorp/terraform-json v0.23.0 github.com/hashicorp/terraform-registry-address v0.2.3 - github.com/hashicorp/terraform-schema v0.0.0-20241128095320-1392f740c4fe + github.com/hashicorp/terraform-schema v0.0.0-20241206161747-7d864b0dfb07 github.com/mcuadros/go-defaults v1.2.0 github.com/mh-cbon/go-fmt-fail v0.0.0-20160815164508-67765b3fbcb5 github.com/mitchellh/cli v1.1.5 diff --git a/go.sum b/go.sum index 0afd82a0..0460d999 100644 --- a/go.sum +++ b/go.sum @@ -228,8 +228,8 @@ github.com/hashicorp/hc-install v0.9.0 h1:2dIk8LcvANwtv3QZLckxcjyF5w8KVtiMxu6G6e github.com/hashicorp/hc-install v0.9.0/go.mod h1:+6vOP+mf3tuGgMApVYtmsnDoKWMDcFXeTxCACYZ8SFg= 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-20241115124434-9d252ff73a68 h1:AhKd5zK/+uiSRCmjpyQqZ8nSDBwiIz2fF5D4nXIYdys= -github.com/hashicorp/hcl-lang v0.0.0-20241115124434-9d252ff73a68/go.mod h1:3HWmoYgqN9HnX3GXCIPbfjLNT48F/0dqY5SP8V9cmIs= +github.com/hashicorp/hcl-lang v0.0.0-20241206161211-32066ec5d37c h1:veJ7q9pXKHKoEkZ9ymSAWv1yUc20X5+qTrwTtuBwYIA= +github.com/hashicorp/hcl-lang v0.0.0-20241206161211-32066ec5d37c/go.mod h1:5NUxE6UGpjm8iW/pY70BJWunDMkc73d7HTtvefU8B/c= github.com/hashicorp/hcl/v2 v2.23.0 h1:Fphj1/gCylPxHutVSEOf2fBOh1VE4AuLV7+kbJf3qos= github.com/hashicorp/hcl/v2 v2.23.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= github.com/hashicorp/terraform-exec v0.21.0 h1:uNkLAe95ey5Uux6KJdua6+cv8asgILFVWkd/RG0D2XQ= @@ -238,8 +238,8 @@ github.com/hashicorp/terraform-json v0.23.0 h1:sniCkExU4iKtTADReHzACkk8fnpQXrdD2 github.com/hashicorp/terraform-json v0.23.0/go.mod h1:MHdXbBAbSg0GvzuWazEGKAn/cyNfIB7mN6y7KJN6y2c= github.com/hashicorp/terraform-registry-address v0.2.3 h1:2TAiKJ1A3MAkZlH1YI/aTVcLZRu7JseiXNRHbOAyoTI= github.com/hashicorp/terraform-registry-address v0.2.3/go.mod h1:lFHA76T8jfQteVfT7caREqguFrW3c4MFSPhZB7HHgUM= -github.com/hashicorp/terraform-schema v0.0.0-20241128095320-1392f740c4fe h1:2pVtzihaLjn6PTIyKom+X491QlLupxGoLRf5Ik8zpYM= -github.com/hashicorp/terraform-schema v0.0.0-20241128095320-1392f740c4fe/go.mod h1:3vDqHlpaMuTeBXSC4LWDM/m2QdEe9DmC90IgyuhdgZw= +github.com/hashicorp/terraform-schema v0.0.0-20241206161747-7d864b0dfb07 h1:jd5luM1tdSpxTk9oY4NrssfFdpprG+nKF9HMpnEQ7to= +github.com/hashicorp/terraform-schema v0.0.0-20241206161747-7d864b0dfb07/go.mod h1:7nHB+a9/PpOTwfdJe/KlYO1VZwGINkv8o7emxaIl3Q4= github.com/hashicorp/terraform-svchost v0.1.1 h1:EZZimZ1GxdqFRinZ1tpJwVxxt49xc/S52uzrw4x0jKQ= github.com/hashicorp/terraform-svchost v0.1.1/go.mod h1:mNsjQfZyf/Jhz35v6/0LWcv26+X7JPS+buii2c9/ctc= github.com/hexops/autogold v1.3.1 h1:YgxF9OHWbEIUjhDbpnLhgVsjUDsiHDTyDfy2lrfdlzo= diff --git a/internal/features/tests/decoder/path_reader.go b/internal/features/tests/decoder/path_reader.go index ccbc3d03..404e41e7 100644 --- a/internal/features/tests/decoder/path_reader.go +++ b/internal/features/tests/decoder/path_reader.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/hcl-lang/decoder" "github.com/hashicorp/hcl-lang/lang" "github.com/hashicorp/hcl-lang/reference" + "github.com/hashicorp/hcl-lang/schema" "github.com/hashicorp/hcl/v2" "github.com/hashicorp/terraform-ls/internal/features/tests/ast" "github.com/hashicorp/terraform-ls/internal/features/tests/state" @@ -52,6 +53,12 @@ var _ decoder.PathReader = &PathReader{} // PathContext returns a PathContext for the given path based on the language ID func (pr *PathReader) PathContext(path lang.Path) (*decoder.PathContext, error) { + if path.File == "" { + panic("file is empty") + // TODO: make this nicer after testing is done + // return nil, fmt.Errorf("file is empty") + } + record, err := pr.StateReader.TestRecordByPath(path.Path) if err != nil { return nil, err @@ -59,7 +66,7 @@ func (pr *PathReader) PathContext(path lang.Path) (*decoder.PathContext, error) switch path.LanguageID { case ilsp.Test.String(): - return testPathContext(record, CombinedReader{ + return testPathContext(record, path.File, CombinedReader{ StateReader: pr.StateReader, ModuleReader: pr.ModuleReader, RootReader: pr.RootReader, @@ -75,7 +82,7 @@ func (pr *PathReader) PathContext(path lang.Path) (*decoder.PathContext, error) return nil, fmt.Errorf("unknown language ID: %q", path.LanguageID) } -func testPathContext(record *state.TestRecord, stateReader CombinedReader) (*decoder.PathContext, error) { +func testPathContext(record *state.TestRecord, filename string, stateReader CombinedReader) (*decoder.PathContext, error) { // TODO! this should only work for terraform 1.6 and above version := stateReader.TerraformVersion(record.Path()) if version == nil { @@ -91,8 +98,7 @@ func testPathContext(record *state.TestRecord, stateReader CombinedReader) (*dec sm.SetStateReader(stateReader) meta := &tftest.Meta{ - Path: record.Path(), - Filenames: record.Meta.Filenames, + Path: record.Path(), } mergedSchema, err := sm.SchemaForTest(meta) @@ -100,31 +106,33 @@ func testPathContext(record *state.TestRecord, stateReader CombinedReader) (*dec return nil, err } + functions, err := functionsForTest(record, version, stateReader) + if err != nil { + return nil, err + } + pathCtx := &decoder.PathContext{ Schema: mergedSchema, ReferenceOrigins: make(reference.Origins, 0), ReferenceTargets: make(reference.Targets, 0), Files: make(map[string]*hcl.File, 0), Validators: validators, - // TODO? functions TFECO-7480 + Functions: functions, } - for _, origin := range record.RefOrigins { + for _, origin := range record.RefOrigins[filename] { if ast.IsTestFilename(origin.OriginRange().Filename) { pathCtx.ReferenceOrigins = append(pathCtx.ReferenceOrigins, origin) } } - for _, target := range record.RefTargets { + for _, target := range record.RefTargets[filename] { if target.RangePtr != nil && ast.IsTestFilename(target.RangePtr.Filename) { pathCtx.ReferenceTargets = append(pathCtx.ReferenceTargets, target) } } - for name, f := range record.ParsedFiles { - if _, ok := name.(ast.TestFilename); ok { - pathCtx.Files[name.String()] = f - } - } + // only one file in this context + pathCtx.Files[filename] = record.ParsedFiles[ast.TestFilename(filename)] return pathCtx, nil } @@ -145,32 +153,44 @@ func mockPathContext(record *state.TestRecord, stateReader CombinedReader) (*dec sm.SetStateReader(stateReader) meta := &tftest.Meta{ - Path: record.Path(), - Filenames: record.Meta.Filenames, + Path: record.Path(), } + // TODO: the schema for mock files gets all the schemas from the test files combined + // while we could know in which test files mocks are used, we want to keep them open + // to all the test files as far as completions go + mergedSchema, err := sm.SchemaForMock(meta) if err != nil { return nil, err } + functions, err := functionsForTest(record, version, stateReader) + if err != nil { + return nil, err + } + pathCtx := &decoder.PathContext{ Schema: mergedSchema, ReferenceOrigins: make(reference.Origins, 0), ReferenceTargets: make(reference.Targets, 0), Files: make(map[string]*hcl.File, 0), Validators: validators, - // TODO? functions TFECO-7480 + Functions: functions, } - for _, origin := range record.RefOrigins { - if ast.IsMockFilename(origin.OriginRange().Filename) { - pathCtx.ReferenceOrigins = append(pathCtx.ReferenceOrigins, origin) + for _, origins := range record.RefOrigins { + for _, origin := range origins { + if ast.IsMockFilename(origin.OriginRange().Filename) { + pathCtx.ReferenceOrigins = append(pathCtx.ReferenceOrigins, origin) + } } } - for _, target := range record.RefTargets { - if target.RangePtr != nil && ast.IsMockFilename(target.RangePtr.Filename) { - pathCtx.ReferenceTargets = append(pathCtx.ReferenceTargets, target) + for _, targets := range record.RefTargets { + for _, target := range targets { + if target.RangePtr != nil && ast.IsMockFilename(target.RangePtr.Filename) { + pathCtx.ReferenceTargets = append(pathCtx.ReferenceTargets, target) + } } } @@ -207,6 +227,7 @@ func (pr *PathReader) Paths(ctx context.Context) []lang.Path { paths = append(paths, lang.Path{ Path: record.Path(), LanguageID: ilsp.Test.String(), + // TODO: add filename! }) } if foundMock { @@ -219,3 +240,12 @@ func (pr *PathReader) Paths(ctx context.Context) []lang.Path { return paths } + +func mustFunctionsForVersion(v *version.Version) map[string]schema.FunctionSignature { + s, err := tfschema.FunctionsForVersion(v) + if err != nil { + // this should never happen + panic(err) + } + return s +} diff --git a/internal/features/tests/decoder/test_functions.go b/internal/features/tests/decoder/test_functions.go new file mode 100644 index 00000000..913c5ed9 --- /dev/null +++ b/internal/features/tests/decoder/test_functions.go @@ -0,0 +1,53 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package decoder + +import ( + "github.com/hashicorp/go-version" + "github.com/hashicorp/hcl-lang/schema" + "github.com/hashicorp/terraform-ls/internal/features/tests/state" + tfaddr "github.com/hashicorp/terraform-registry-address" + tfmod "github.com/hashicorp/terraform-schema/module" + tfschema "github.com/hashicorp/terraform-schema/schema" +) + +func functionsForTest(record *state.TestRecord, version *version.Version, stateReader CombinedReader) (map[string]schema.FunctionSignature, error) { + fm := tfschema.NewFunctionsMerger(mustFunctionsForVersion(version)) + fm.SetTerraformVersion(version) + fm.SetStateReader(stateReader) + + // providers used := stateReader.ModuleReader.LocalModuleMeta(record.Path()) ... + // get module meta for "root" module under test (possibly in parent dir) + // get module meta for all modules used in run blocks in record (Or the keyed on for the current test file!) + + // maybe persist the "module under test"-path in state? makes it just a single lookup then + // needs to happen in a job that depends on module discovery done (so we know whether there really isn't code in the same dir e.g.) + + // FIXME: PathContext assumes all hcl files in a dir are merged, but that's not the case for TF test + // something probably needs a change in hcl lang so it cleanly supports hcl files that are NOT merged + + // TODO: re-enable once we have a way to get provider requirements + // We have to create the provider requirements and references based on the types the functions merger expects + providerRequirements := make(tfmod.ProviderRequirements) //, len(record.Meta.ProviderRequirements)) + providerReferences := make(map[tfmod.ProviderRef]tfaddr.Provider) //, len(record.Meta.ProviderRequirements)) + // for localName, req := range record.Meta.ProviderRequirements { + // providerRequirements[req.Source] = req.VersionConstraints + // providerReferences[tfmod.ProviderRef{LocalName: localName}] = req.Source + // } + + // TODO: steps + // 1. find the related terraform module as that's defining which providers are available for functions + // (needs to be in terraform sources, i think; todo: check if implicit provider refs also work) + + // given a test record (which points to a test directory), + // + + mMeta := &tfmod.Meta{ + Path: record.Path(), + ProviderRequirements: providerRequirements, + ProviderReferences: providerReferences, + } + + return fm.FunctionsForModule(mMeta) +} diff --git a/internal/features/tests/jobs/metadata.go b/internal/features/tests/jobs/metadata.go index da86a125..b5cd0b89 100644 --- a/internal/features/tests/jobs/metadata.go +++ b/internal/features/tests/jobs/metadata.go @@ -6,11 +6,13 @@ package jobs import ( "context" + "github.com/hashicorp/hcl/v2" "github.com/hashicorp/terraform-ls/internal/document" "github.com/hashicorp/terraform-ls/internal/features/tests/state" "github.com/hashicorp/terraform-ls/internal/job" "github.com/hashicorp/terraform-ls/internal/terraform/module/operation" earlydecoder "github.com/hashicorp/terraform-schema/earlydecoder/tests" + "github.com/hashicorp/terraform-schema/test" ) // LoadTestMetadata loads data about the test in a version-independent @@ -34,8 +36,15 @@ func LoadTestMetadata(ctx context.Context, testStore *state.TestStore, testPath return err } + var diags hcl.Diagnostics + meta := map[string]*test.Meta{} + for filename, file := range record.ParsedFiles.AsMap() { + fMeta, fDiags := earlydecoder.LoadTest(record.Path(), filename, file) + diags = append(diags, fDiags...) + meta[filename] = fMeta + } + var mErr error - meta, diags := earlydecoder.LoadTest(record.Path(), record.ParsedFiles.AsMap()) if len(diags) > 0 { mErr = diags } diff --git a/internal/features/tests/jobs/references.go b/internal/features/tests/jobs/references.go index 4fad6ed5..94aba313 100644 --- a/internal/features/tests/jobs/references.go +++ b/internal/features/tests/jobs/references.go @@ -11,6 +11,7 @@ import ( "github.com/hashicorp/hcl-lang/reference" idecoder "github.com/hashicorp/terraform-ls/internal/decoder" "github.com/hashicorp/terraform-ls/internal/document" + "github.com/hashicorp/terraform-ls/internal/features/tests/ast" fdecoder "github.com/hashicorp/terraform-ls/internal/features/tests/decoder" "github.com/hashicorp/terraform-ls/internal/features/tests/state" "github.com/hashicorp/terraform-ls/internal/job" @@ -27,7 +28,7 @@ import ( // can be referred to as var.foobar. This is useful e.g. during completion, // go-to-definition or go-to-references. func DecodeReferenceTargets(ctx context.Context, testStore *state.TestStore, testPath string, moduleFeature fdecoder.ModuleReader, rootFeature fdecoder.RootReader) error { - mod, err := testStore.TestRecordByPath(testPath) + record, err := testStore.TestRecordByPath(testPath) if err != nil { return err } @@ -35,7 +36,7 @@ func DecodeReferenceTargets(ctx context.Context, testStore *state.TestStore, tes // TODO: Avoid collection if upstream jobs reported no changes // Avoid collection if it is already in progress or already done - if mod.RefTargetsState != op.OpStateUnknown && !job.IgnoreState(ctx) { + if record.RefTargetsState != op.OpStateUnknown && !job.IgnoreState(ctx) { return job.StateNotChangedErr{Dir: document.DirHandleFromPath(testPath)} } @@ -51,14 +52,24 @@ func DecodeReferenceTargets(ctx context.Context, testStore *state.TestStore, tes }) d.SetContext(idecoder.DecoderContext(ctx)) - testDecoder, err := d.Path(lang.Path{ - Path: testPath, - LanguageID: ilsp.Test.String(), - }) - if err != nil { - return err + // loop through all parsed files and collect targets separately for all test files + testTargets := make(map[string]reference.Targets, 0) + var rErr error + for name := range record.ParsedFiles { + if _, ok := name.(ast.TestFilename); ok { + testDecoder, err := d.Path(lang.Path{ + Path: testPath, + LanguageID: ilsp.Test.String(), + File: name.String(), + }) + if err != nil { + return err + } + targets, err := testDecoder.CollectReferenceTargets() + testTargets[name.String()] = targets + rErr = err + } } - testTargets, rErr := testDecoder.CollectReferenceTargets() mockDecoder, err := d.Path(lang.Path{ Path: testPath, @@ -69,11 +80,7 @@ func DecodeReferenceTargets(ctx context.Context, testStore *state.TestStore, tes } mockTargets, rErr := mockDecoder.CollectReferenceTargets() - targets := make(reference.Targets, 0) - targets = append(targets, testTargets...) - targets = append(targets, mockTargets...) - - sErr := testStore.UpdateReferenceTargets(testPath, targets, rErr) + sErr := testStore.UpdateReferenceTargets(testPath, testTargets, mockTargets, rErr) if sErr != nil { return sErr } @@ -90,7 +97,7 @@ func DecodeReferenceTargets(ctx context.Context, testStore *state.TestStore, tes // at a particular LOC. This can be later matched with targets // (as obtained via [DecodeReferenceTargets]) during hover or go-to-definition. func DecodeReferenceOrigins(ctx context.Context, testStore *state.TestStore, testPath string, moduleFeature fdecoder.ModuleReader, rootFeature fdecoder.RootReader) error { - mod, err := testStore.TestRecordByPath(testPath) + record, err := testStore.TestRecordByPath(testPath) if err != nil { return err } @@ -98,7 +105,7 @@ func DecodeReferenceOrigins(ctx context.Context, testStore *state.TestStore, tes // TODO: Avoid collection if upstream jobs reported no changes // Avoid collection if it is already in progress or already done - if mod.RefOriginsState != op.OpStateUnknown && !job.IgnoreState(ctx) { + if record.RefOriginsState != op.OpStateUnknown && !job.IgnoreState(ctx) { return job.StateNotChangedErr{Dir: document.DirHandleFromPath(testPath)} } @@ -114,14 +121,24 @@ func DecodeReferenceOrigins(ctx context.Context, testStore *state.TestStore, tes }) d.SetContext(idecoder.DecoderContext(ctx)) - testDecoder, err := d.Path(lang.Path{ - Path: testPath, - LanguageID: ilsp.Test.String(), - }) - if err != nil { - return err + // loop through all parsed files and collect targets separately for all test files + testOrigins := make(map[string]reference.Origins, 0) + var rErr error + for name := range record.ParsedFiles { + if _, ok := name.(ast.TestFilename); ok { + testDecoder, err := d.Path(lang.Path{ + Path: testPath, + LanguageID: ilsp.Test.String(), + File: name.String(), + }) + if err != nil { + return err + } + origins, err := testDecoder.CollectReferenceOrigins() + testOrigins[name.String()] = origins + rErr = err + } } - testOrigins, _ := testDecoder.CollectReferenceOrigins() mockDecoder, err := d.Path(lang.Path{ Path: testPath, @@ -132,11 +149,7 @@ func DecodeReferenceOrigins(ctx context.Context, testStore *state.TestStore, tes } mockOrigins, rErr := mockDecoder.CollectReferenceOrigins() - origins := make(reference.Origins, 0) - origins = append(origins, testOrigins...) - origins = append(origins, mockOrigins...) - - sErr := testStore.UpdateReferenceOrigins(testPath, origins, rErr) + sErr := testStore.UpdateReferenceOrigins(testPath, testOrigins, mockOrigins, rErr) if sErr != nil { return sErr } diff --git a/internal/features/tests/jobs/validation.go b/internal/features/tests/jobs/validation.go index 94241ee1..4f3d8f25 100644 --- a/internal/features/tests/jobs/validation.go +++ b/internal/features/tests/jobs/validation.go @@ -54,6 +54,7 @@ func SchemaTestValidation(ctx context.Context, testStore *state.TestStore, testP testDecoder, err := d.Path(lang.Path{ Path: testPath, LanguageID: ilsp.Test.String(), + // TODO: do this individually for each file }) if err != nil { return err diff --git a/internal/features/tests/state/test_meta.go b/internal/features/tests/state/test_meta.go index ef25927a..839c77ab 100644 --- a/internal/features/tests/state/test_meta.go +++ b/internal/features/tests/state/test_meta.go @@ -6,13 +6,23 @@ package state // TestMetadata contains the result of the early decoding of a test, // it will be used obtain the correct provider and related module schemas type TestMetadata struct { - Filenames []string + + // ProviderRequirements map[string]ProviderRequirement + // MockProviders map[string]MockProvider + + // TODO: need to collect run blocks and use their module source for kicking of decoding for terraform files on those dirs + // TODO: testrecord needs to be file not directory based (because the are not merged) (this might be hard) + // alternatively: another layer for metadata (key them by filename) + + RunBlocks []string // TODO: just for testing, change to proper type + + // Components map[string]Component + // Variables map[string]Variable + // Outputs map[string]Output } func (tm TestMetadata) Copy() TestMetadata { - newTm := TestMetadata{ - Filenames: tm.Filenames, - } + newTm := TestMetadata{} return newTm } diff --git a/internal/features/tests/state/test_record.go b/internal/features/tests/state/test_record.go index 844ff093..35c6ac27 100644 --- a/internal/features/tests/state/test_record.go +++ b/internal/features/tests/state/test_record.go @@ -17,17 +17,20 @@ type TestRecord struct { PreloadEmbeddedSchemaState op.OpState - Meta TestMetadata + // Mapping of filename to Metadata (same for the other maps below) + Meta map[string]TestMetadata MetaErr error MetaState op.OpState - RefTargets reference.Targets - RefTargetsErr error - RefTargetsState op.OpState + GlobalRefTargets reference.Targets // these are global targets that are not tied to a specific file + RefTargets map[string]reference.Targets + RefTargetsErr error + RefTargetsState op.OpState - RefOrigins reference.Origins - RefOriginsErr error - RefOriginsState op.OpState + GlobalRefOrigins reference.Origins // these are global origins that are not tied to a specific file + RefOrigins map[string]reference.Origins + RefOriginsErr error + RefOriginsState op.OpState ParsedFiles ast.Files ParsingErr error @@ -49,15 +52,12 @@ func (m *TestRecord) Copy() *TestRecord { PreloadEmbeddedSchemaState: m.PreloadEmbeddedSchemaState, - RefTargets: m.RefTargets.Copy(), RefTargetsErr: m.RefTargetsErr, RefTargetsState: m.RefTargetsState, - RefOrigins: m.RefOrigins.Copy(), RefOriginsErr: m.RefOriginsErr, RefOriginsState: m.RefOriginsState, - Meta: m.Meta.Copy(), MetaErr: m.MetaErr, MetaState: m.MetaState, @@ -65,6 +65,27 @@ func (m *TestRecord) Copy() *TestRecord { DiagnosticsState: m.DiagnosticsState.Copy(), } + if m.RefTargets != nil { + newRecord.RefTargets = make(map[string]reference.Targets, len(m.RefTargets)) + for name, targets := range m.RefTargets { + newRecord.RefTargets[name] = targets.Copy() + } + } + + if m.RefOrigins != nil { + newRecord.RefOrigins = make(map[string]reference.Origins, len(m.RefOrigins)) + for name, origins := range m.RefOrigins { + newRecord.RefOrigins[name] = origins.Copy() + } + } + + if m.Meta != nil { + newRecord.Meta = make(map[string]TestMetadata, len(m.Meta)) + for name, meta := range m.Meta { + newRecord.Meta[name] = meta.Copy() + } + } + if m.ParsedFiles != nil { newRecord.ParsedFiles = make(ast.Files, len(m.ParsedFiles)) for name, f := range m.ParsedFiles { diff --git a/internal/features/tests/state/test_store.go b/internal/features/tests/state/test_store.go index bda723ec..6aab49d4 100644 --- a/internal/features/tests/state/test_store.go +++ b/internal/features/tests/state/test_store.go @@ -227,7 +227,7 @@ func (s *TestStore) SetMetaState(path string, state op.OpState) error { return nil } -func (s *TestStore) UpdateMetadata(path string, meta *tftest.Meta, mErr error) error { +func (s *TestStore) UpdateMetadata(path string, meta map[string]*tftest.Meta, mErr error) error { txn := s.db.Txn(true) txn.Defer(func() { s.SetMetaState(path, op.OpStateLoaded) @@ -240,11 +240,14 @@ func (s *TestStore) UpdateMetadata(path string, meta *tftest.Meta, mErr error) e } record := oldRecord.Copy() - record.Meta = TestMetadata{ - Filenames: meta.Filenames, - } record.MetaErr = mErr + for filename, m := range meta { + record.Meta[filename] = TestMetadata{ + RunBlocks: m.RunBlocks, + } + } + err = txn.Insert(s.tableName, record) if err != nil { return err @@ -328,7 +331,7 @@ func (s *TestStore) SetReferenceTargetsState(path string, state op.OpState) erro return nil } -func (s *TestStore) UpdateReferenceTargets(path string, refs reference.Targets, rErr error) error { +func (s *TestStore) UpdateReferenceTargets(path string, refs map[string]reference.Targets, globalRefs reference.Targets, rErr error) error { txn := s.db.Txn(true) txn.Defer(func() { s.SetReferenceTargetsState(path, op.OpStateLoaded) @@ -340,6 +343,7 @@ func (s *TestStore) UpdateReferenceTargets(path string, refs reference.Targets, return err } + mod.GlobalRefTargets = globalRefs mod.RefTargets = refs mod.RefTargetsErr = rErr @@ -371,7 +375,7 @@ func (s *TestStore) SetReferenceOriginsState(path string, state op.OpState) erro return nil } -func (s *TestStore) UpdateReferenceOrigins(path string, origins reference.Origins, roErr error) error { +func (s *TestStore) UpdateReferenceOrigins(path string, origins map[string]reference.Origins, globalOrigins reference.Origins, roErr error) error { txn := s.db.Txn(true) txn.Defer(func() { s.SetReferenceOriginsState(path, op.OpStateLoaded) @@ -383,6 +387,7 @@ func (s *TestStore) UpdateReferenceOrigins(path string, origins reference.Origin return err } + mod.GlobalRefOrigins = globalOrigins mod.RefOrigins = origins mod.RefOriginsErr = roErr diff --git a/internal/langserver/handlers/code_lens.go b/internal/langserver/handlers/code_lens.go index a83197b3..8988694f 100644 --- a/internal/langserver/handlers/code_lens.go +++ b/internal/langserver/handlers/code_lens.go @@ -29,6 +29,7 @@ func (svc *service) TextDocumentCodeLens(ctx context.Context, params lsp.CodeLen path := lang.Path{ Path: doc.Dir.Path(), LanguageID: doc.LanguageID, + File: doc.Filename, } lenses, err := svc.decoder.CodeLensesForFile(ctx, path, doc.Filename) diff --git a/internal/langserver/handlers/go_to_ref_target.go b/internal/langserver/handlers/go_to_ref_target.go index 62102ab6..e7074e1f 100644 --- a/internal/langserver/handlers/go_to_ref_target.go +++ b/internal/langserver/handlers/go_to_ref_target.go @@ -61,6 +61,7 @@ func (svc *service) goToReferenceTarget(ctx context.Context, params lsp.TextDocu path := lang.Path{ Path: doc.Dir.Path(), LanguageID: doc.LanguageID, + File: doc.Filename, // TODO: this might have consequences for non test files } return svc.decoder.ReferenceTargetsForOriginAtPos(path, doc.Filename, pos) diff --git a/internal/langserver/handlers/references.go b/internal/langserver/handlers/references.go index d5ba6a0c..4dd14684 100644 --- a/internal/langserver/handlers/references.go +++ b/internal/langserver/handlers/references.go @@ -34,6 +34,7 @@ func (svc *service) References(ctx context.Context, params lsp.ReferenceParams) path := lang.Path{ Path: doc.Dir.Path(), LanguageID: doc.LanguageID, + File: doc.Filename, // TODO: this might have consequences for non test files } // TODO? maybe kick off indexing of the whole workspace here origins := svc.decoder.ReferenceOriginsTargetingPos(path, doc.Filename, pos) diff --git a/internal/langserver/handlers/service.go b/internal/langserver/handlers/service.go index b74f0f0a..56e335aa 100644 --- a/internal/langserver/handlers/service.go +++ b/internal/langserver/handlers/service.go @@ -750,5 +750,6 @@ func (svc *service) decoderForDocument(_ context.Context, doc *document.Document return svc.decoder.Path(lang.Path{ Path: doc.Dir.Path(), LanguageID: doc.LanguageID, + File: doc.Filename, // TODO: this might have consequences for non test files }) }