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

(TF-18664) Add DecodeReferenceOrigins and DecodeReferenceTargets jobs #1786

Merged
merged 2 commits into from
Aug 6, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/unreleased/ENHANCEMENTS-20240805-140526.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: ENHANCEMENTS
body: Add DecodeReferenceOrigins and DecodeReferenceTargets jobs
time: 2024-08-05T14:05:26.030294-04:00
custom:
Issue: "1786"
Repository: terraform-ls
26 changes: 26 additions & 0 deletions internal/features/stacks/decoder/path_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,19 @@ func stackPathContext(record *state.StackRecord, stateReader CombinedReader) (*d
}

// TODO: Add reference origins and targets if needed
for _, origin := range record.RefOrigins {
if ast.IsStackFilename(origin.OriginRange().Filename) {
pathCtx.ReferenceOrigins = append(pathCtx.ReferenceOrigins, origin)
}
}

for _, target := range record.RefTargets {
if target.RangePtr != nil && ast.IsStackFilename(target.RangePtr.Filename) {
pathCtx.ReferenceTargets = append(pathCtx.ReferenceTargets, target)
} else if target.RangePtr == nil {
pathCtx.ReferenceTargets = append(pathCtx.ReferenceTargets, target)
}
}

for name, f := range record.ParsedFiles {
if _, ok := name.(ast.StackFilename); ok {
Expand Down Expand Up @@ -153,6 +166,19 @@ func deployPathContext(record *state.StackRecord) (*decoder.PathContext, error)
}

// TODO: Add reference origins and targets if needed
for _, origin := range record.RefOrigins {
if ast.IsDeployFilename(origin.OriginRange().Filename) {
pathCtx.ReferenceOrigins = append(pathCtx.ReferenceOrigins, origin)
}
}

for _, target := range record.RefTargets {
if target.RangePtr != nil && ast.IsDeployFilename(target.RangePtr.Filename) {
pathCtx.ReferenceTargets = append(pathCtx.ReferenceTargets, target)
} else if target.RangePtr == nil {
pathCtx.ReferenceTargets = append(pathCtx.ReferenceTargets, target)
}
ansgarm marked this conversation as resolved.
Show resolved Hide resolved
}

for name, f := range record.ParsedFiles {
if _, ok := name.(ast.DeployFilename); ok {
Expand Down
63 changes: 45 additions & 18 deletions internal/features/stacks/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -192,10 +192,6 @@ func (f *StacksFeature) decodeStack(ctx context.Context, dir document.DirHandle,
}
ids = append(ids, parseId)

// this needs to be here because the setting context
// is not available in the validate job
validationOptions, _ := lsctx.ValidationOptions(ctx)

metaId, err := f.stateStore.JobStore.EnqueueJob(ctx, job.Job{
Dir: dir,
Func: func(ctx context.Context) error {
Expand Down Expand Up @@ -238,21 +234,33 @@ func (f *StacksFeature) decodeStack(ctx context.Context, dir document.DirHandle,
}
deferIds = append(deferIds, eSchemaId)

if validationOptions.EnableEnhancedValidation {
validationId, err := f.stateStore.JobStore.EnqueueJob(ctx, job.Job{
Dir: dir,
Func: func(ctx context.Context) error {
return jobs.SchemaStackValidation(ctx, f.store, f.moduleFeature, dir.Path())
},
Type: operation.OpTypeSchemaStackValidation.String(),
DependsOn: deferIds,
IgnoreState: ignoreState,
})
if err != nil {
return deferIds, err
}
deferIds = append(deferIds, validationId)
refTargetsId, err := f.stateStore.JobStore.EnqueueJob(ctx, job.Job{
Dir: dir,
Func: func(ctx context.Context) error {
return jobs.DecodeReferenceTargets(ctx, f.store, f.moduleFeature, path)
},
Type: operation.OpTypeDecodeReferenceTargets.String(),
DependsOn: job.IDs{eSchemaId},
IgnoreState: ignoreState,
})
if err != nil {
return deferIds, err
}
deferIds = append(deferIds, refTargetsId)

refOriginsId, err := f.stateStore.JobStore.EnqueueJob(ctx, job.Job{
Dir: dir,
Func: func(ctx context.Context) error {
return jobs.DecodeReferenceOrigins(ctx, f.store, f.moduleFeature, path)
},
Type: operation.OpTypeDecodeReferenceOrigins.String(),
DependsOn: job.IDs{eSchemaId},
IgnoreState: ignoreState,
})
if err != nil {
return deferIds, err
}
deferIds = append(deferIds, refOriginsId)

return deferIds, nil
},
Expand All @@ -262,6 +270,25 @@ func (f *StacksFeature) decodeStack(ctx context.Context, dir document.DirHandle,
}
ids = append(ids, metaId)

validationOptions, err := lsctx.ValidationOptions(ctx)
if err != nil {
return ids, err
}
if validationOptions.EnableEnhancedValidation {
_, err := f.stateStore.JobStore.EnqueueJob(ctx, job.Job{
Dir: dir,
Func: func(ctx context.Context) error {
return jobs.SchemaStackValidation(ctx, f.store, f.moduleFeature, dir.Path())
},
Type: operation.OpTypeSchemaStackValidation.String(),
DependsOn: ids,
IgnoreState: ignoreState,
})
if err != nil {
return ids, err
}
}
ansgarm marked this conversation as resolved.
Show resolved Hide resolved

// TODO: Implement the following functions where appropriate to stacks
// Future: decodeDeclaredModuleCalls(ctx, dir, ignoreState)
// Future: DecodeReferenceTargets(ctx, f.Store, f.rootFeature, path)
Expand Down
143 changes: 143 additions & 0 deletions internal/features/stacks/jobs/references.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: MPL-2.0

package jobs

import (
"context"

"github.com/hashicorp/hcl-lang/decoder"
"github.com/hashicorp/hcl-lang/lang"
"github.com/hashicorp/hcl-lang/reference"
idecoder "github.com/hashicorp/terraform-ls/internal/decoder"
"github.com/hashicorp/terraform-ls/internal/document"
sdecoder "github.com/hashicorp/terraform-ls/internal/features/stacks/decoder"
"github.com/hashicorp/terraform-ls/internal/features/stacks/state"
"github.com/hashicorp/terraform-ls/internal/job"
ilsp "github.com/hashicorp/terraform-ls/internal/lsp"
"github.com/hashicorp/terraform-ls/internal/terraform/module/operation"
)

// DecodeReferenceTargets collects reference targets,
// using previously parsed AST (via [ParseStackConfiguration]),
// core schema of appropriate version (as obtained via [GetTerraformVersion])
// and provider schemas ([PreloadEmbeddedSchema] or [ObtainSchema]).
//
// For example it tells us that variable block between certain LOC
// 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, stackStore *state.StackStore, moduleReader sdecoder.ModuleReader, stackPath string) error {
mod, err := stackStore.StackRecordByPath(stackPath)
if err != nil {
return err
}

// TODO: Avoid collection if upstream jobs reported no changes

// Avoid collection if it is already in progress or already done
if mod.RefTargetsState != operation.OpStateUnknown && !job.IgnoreState(ctx) {
return job.StateNotChangedErr{Dir: document.DirHandleFromPath(stackPath)}
}

err = stackStore.SetReferenceTargetsState(stackPath, operation.OpStateLoading)
if err != nil {
return err
}

d := decoder.NewDecoder(&sdecoder.PathReader{
StateReader: stackStore,
ModuleReader: moduleReader,
})
d.SetContext(idecoder.DecoderContext(ctx))

stackDecoder, err := d.Path(lang.Path{
Path: stackPath,
LanguageID: ilsp.Stacks.String(),
})
if err != nil {
return err
}
stackTargets, rErr := stackDecoder.CollectReferenceTargets()

deployDecoder, err := d.Path(lang.Path{
Path: stackPath,
LanguageID: ilsp.Deploy.String(),
})
if err != nil {
return err
}
deployTargets, rErr := deployDecoder.CollectReferenceTargets()

targets := make(reference.Targets, 0)
targets = append(targets, stackTargets...)
targets = append(targets, deployTargets...)

sErr := stackStore.UpdateReferenceTargets(stackPath, targets, rErr)
if sErr != nil {
return sErr
}

return rErr
}

// DecodeReferenceOrigins collects reference origins,
// using previously parsed AST (via [ParseStackConfiguration]),
// core schema of appropriate version (as obtained via [GetTerraformVersion])
// and provider schemas ([PreloadEmbeddedSchema] or [ObtainSchema]).
//
// For example it tells us that there is a reference address var.foobar
// 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, stackStore *state.StackStore, moduleReader sdecoder.ModuleReader, stackPath string) error {
mod, err := stackStore.StackRecordByPath(stackPath)
if err != nil {
return err
}

// TODO: Avoid collection if upstream jobs reported no changes

// Avoid collection if it is already in progress or already done
if mod.RefOriginsState != operation.OpStateUnknown && !job.IgnoreState(ctx) {
return job.StateNotChangedErr{Dir: document.DirHandleFromPath(stackPath)}
}

err = stackStore.SetReferenceOriginsState(stackPath, operation.OpStateLoading)
if err != nil {
return err
}

d := decoder.NewDecoder(&sdecoder.PathReader{
StateReader: stackStore,
ModuleReader: moduleReader,
})
d.SetContext(idecoder.DecoderContext(ctx))

stackDecoder, err := d.Path(lang.Path{
Path: stackPath,
LanguageID: ilsp.Stacks.String(),
})
if err != nil {
return err
}
stackOrigins, rErr := stackDecoder.CollectReferenceOrigins()

deployDecoder, err := d.Path(lang.Path{
Path: stackPath,
LanguageID: ilsp.Deploy.String(),
})
if err != nil {
return err
}
deployOrigins, rErr := deployDecoder.CollectReferenceOrigins()

origins := make(reference.Origins, 0)
origins = append(origins, stackOrigins...)
origins = append(origins, deployOrigins...)

sErr := stackStore.UpdateReferenceOrigins(stackPath, origins, rErr)
if sErr != nil {
return sErr
}

return rErr
}
25 changes: 23 additions & 2 deletions internal/features/stacks/state/stack_record.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package state

import (
"github.com/hashicorp/go-version"
"github.com/hashicorp/hcl-lang/reference"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform-ls/internal/features/stacks/ast"
globalAst "github.com/hashicorp/terraform-ls/internal/terraform/ast"
Expand Down Expand Up @@ -34,6 +35,14 @@ type StackRecord struct {
RequiredTerraformVersion *version.Version
RequiredTerraformVersionErr error
RequiredTerraformVersionState operation.OpState

RefTargets reference.Targets
RefTargetsErr error
RefTargetsState operation.OpState

RefOrigins reference.Origins
RefOriginsErr error
RefOriginsState operation.OpState
}

func (m *StackRecord) Path() string {
Expand All @@ -59,6 +68,14 @@ func (m *StackRecord) Copy() *StackRecord {
RequiredTerraformVersion: m.RequiredTerraformVersion,
RequiredTerraformVersionErr: m.RequiredTerraformVersionErr,
RequiredTerraformVersionState: m.RequiredTerraformVersionState,

RefTargets: m.RefTargets.Copy(),
RefTargetsErr: m.RefTargetsErr,
RefTargetsState: m.RefTargetsState,

RefOrigins: m.RefOrigins.Copy(),
RefOriginsErr: m.RefOriginsErr,
RefOriginsState: m.RefOriginsState,
}

if m.ParsedFiles != nil {
Expand All @@ -85,9 +102,13 @@ func (m *StackRecord) Copy() *StackRecord {
return newRecord
}

func newStack(modPath string) *StackRecord {
func newStack(stackPath string) *StackRecord {
return &StackRecord{
path: modPath,
path: stackPath,
PreloadEmbeddedSchemaState: operation.OpStateUnknown,
RefOriginsState: operation.OpStateUnknown,
RefTargetsState: operation.OpStateUnknown,
MetaState: operation.OpStateUnknown,
DiagnosticsState: globalAst.DiagnosticSourceState{
globalAst.HCLParsingSource: operation.OpStateUnknown,
globalAst.SchemaValidationSource: operation.OpStateUnknown,
Expand Down
Loading