Skip to content

Commit

Permalink
Update ParseVariables for single files
Browse files Browse the repository at this point in the history
Modifies ParsedVarsFiles to only re-parse the single tfvars file which is being changed, if the job was scheduled as part of `textDocument/didChange` request.

This is a follow up to #1404 which updated the parsing job for terraform files.
  • Loading branch information
jpogran committed Sep 28, 2023
1 parent a3b9509 commit d9cae9e
Show file tree
Hide file tree
Showing 5 changed files with 149 additions and 5 deletions.
16 changes: 16 additions & 0 deletions internal/terraform/ast/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,14 @@ func VarsFilesFromMap(m map[string]*hcl.File) VarsFiles {
return mf
}

func (mf VarsFiles) Copy() VarsFiles {
m := make(VarsFiles, len(mf))
for name, file := range mf {
m[name] = file
}
return m
}

type VarsDiags map[VarsFilename]hcl.Diagnostics

func VarsDiagsFromMap(m map[string]hcl.Diagnostics) VarsDiags {
Expand All @@ -62,6 +70,14 @@ func VarsDiagsFromMap(m map[string]hcl.Diagnostics) VarsDiags {
return mf
}

func (mf VarsDiags) Copy() VarsDiags {
m := make(VarsDiags, len(mf))
for name, file := range mf {
m[name] = file
}
return m
}

func (vd VarsDiags) AutoloadedOnly() VarsDiags {
diags := make(VarsDiags)
for name, f := range vd {
Expand Down
48 changes: 43 additions & 5 deletions internal/terraform/module/module_ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -440,20 +440,58 @@ func ParseVariables(ctx context.Context, fs ReadOnlyFS, modStore *state.ModuleSt

// TODO: Avoid parsing if the content matches existing AST

// TODO: Only parse the file that's being changed/opened, unless this is 1st-time parsing

// Avoid parsing if it is already in progress or already known
if mod.VarsDiagnosticsState[ast.HCLParsingSource] != op.OpStateUnknown && !job.IgnoreState(ctx) {
return job.StateNotChangedErr{Dir: document.DirHandleFromPath(modPath)}
}

err = modStore.SetVarsDiagnosticsState(modPath, ast.HCLParsingSource, op.OpStateLoading)
var files ast.VarsFiles
var diags ast.VarsDiags
rpcContext := lsctx.RPCContext(ctx)
// Only parse the file that's being changed/opened, unless this is 1st-time parsing
if mod.ModuleDiagnosticsState[ast.HCLParsingSource] == op.OpStateLoaded && rpcContext.IsDidChangeRequest() && lsctx.IsLanguageId(ctx, ilsp.Tfvars.String()) {
// the file has already been parsed, so only examine this file and not the whole module
err = modStore.SetVarsDiagnosticsState(modPath, ast.HCLParsingSource, op.OpStateLoading)
if err != nil {
return err
}
filePath, err := uri.PathFromURI(rpcContext.URI)
if err != nil {
return err
}
fileName := filepath.Base(filePath)

f, vdiags, err := parser.ParseVariableFile(fs, filePath)
if err != nil {
return err
}

existingFiles := mod.ParsedVarsFiles.Copy()
existingFiles[ast.VarsFilename(fileName)] = f
files = existingFiles

existingDiags, ok := mod.VarsDiagnostics[ast.HCLParsingSource]
if !ok {
existingDiags = make(ast.VarsDiags)
} else {
existingDiags = existingDiags.Copy()
}
existingDiags[ast.VarsFilename(fileName)] = vdiags
diags = existingDiags
} else {
// this is the first time file is opened so parse the whole module
err = modStore.SetVarsDiagnosticsState(modPath, ast.HCLParsingSource, op.OpStateLoading)
if err != nil {
return err
}

files, diags, err = parser.ParseVariableFiles(fs, modPath)
}

if err != nil {
return err
}

files, diags, err := parser.ParseVariableFiles(fs, modPath)

sErr := modStore.UpdateParsedVarsFiles(modPath, files, err)
if sErr != nil {
return sErr
Expand Down
72 changes: 72 additions & 0 deletions internal/terraform/module/module_ops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,7 @@ func TestParseModuleConfiguration(t *testing.T) {
t.Fatal("diags should match")
}
}

func TestParseModuleConfiguration_ignore_tfvars(t *testing.T) {
ctx := context.Background()
ss, err := state.NewStateStore()
Expand Down Expand Up @@ -1088,6 +1089,77 @@ func TestParseModuleConfiguration_ignore_tfvars(t *testing.T) {
}
}

func TestParseVariables(t *testing.T) {
ctx := context.Background()
ss, err := state.NewStateStore()
if err != nil {
t.Fatal(err)
}

testData, err := filepath.Abs("testdata")
if err != nil {
t.Fatal(err)
}
testFs := filesystem.NewFilesystem(ss.DocumentStore)

singleFileModulePath := filepath.Join(testData, "single-file-change-module")

err = ss.Modules.Add(singleFileModulePath)
if err != nil {
t.Fatal(err)
}

ctx = lsctx.WithLanguageId(ctx, ilsp.Tfvars.String())
ctx = lsctx.WithRPCContext(ctx, lsctx.RPCContextData{})
err = ParseModuleConfiguration(ctx, testFs, ss.Modules, singleFileModulePath)
if err != nil {
t.Fatal(err)
}
err = ParseVariables(ctx, testFs, ss.Modules, singleFileModulePath)
if err != nil {
t.Fatal(err)
}

before, err := ss.Modules.ModuleByPath(singleFileModulePath)
if err != nil {
t.Fatal(err)
}

// say we're coming from did_change request
fooURI, err := filepath.Abs(filepath.Join(singleFileModulePath, "example.tfvars"))
if err != nil {
t.Fatal(err)
}
x := lsctx.RPCContextData{
Method: "textDocument/didChange",
URI: uri.FromPath(fooURI),
}

// ignore job state
ctx = job.WithIgnoreState(ctx, true)
ctx = lsctx.WithLanguageId(ctx, ilsp.Tfvars.String())
ctx = lsctx.WithRPCContext(ctx, x)
err = ParseVariables(ctx, testFs, ss.Modules, singleFileModulePath)
if err != nil {
t.Fatal(err)
}

after, err := ss.Modules.ModuleByPath(singleFileModulePath)
if err != nil {
t.Fatal(err)
}

// example.tfvars should not be the same as first seen
if before.ParsedVarsFiles["example.tfvars"] == after.ParsedVarsFiles["example.tfvars"] {
t.Fatal("file should mismatch")
}

// // diags should change for example.tfvars
if before.VarsDiagnostics["example.tfvars"][0] == after.VarsDiagnostics["example.tfvars"][0] {

Check failure on line 1158 in internal/terraform/module/module_ops_test.go

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest)

invalid operation: before.VarsDiagnostics["example.tfvars"][0] == after.VarsDiagnostics["example.tfvars"][0] (slice can only be compared to nil)

Check failure on line 1158 in internal/terraform/module/module_ops_test.go

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest)

cannot use "example.tfvars" (untyped string constant) as "github.com/hashicorp/terraform-ls/internal/terraform/ast".DiagnosticSource value in map index

Check failure on line 1158 in internal/terraform/module/module_ops_test.go

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest)

cannot use 0 (untyped int constant) as "github.com/hashicorp/terraform-ls/internal/terraform/ast".VarsFilename value in map index

Check failure on line 1158 in internal/terraform/module/module_ops_test.go

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest)

cannot use "example.tfvars" (untyped string constant) as "github.com/hashicorp/terraform-ls/internal/terraform/ast".DiagnosticSource value in map index

Check failure on line 1158 in internal/terraform/module/module_ops_test.go

View workflow job for this annotation

GitHub Actions / test (ubuntu-latest)

cannot use 0 (untyped int constant) as "github.com/hashicorp/terraform-ls/internal/terraform/ast".VarsFilename value in map index

Check failure on line 1158 in internal/terraform/module/module_ops_test.go

View workflow job for this annotation

GitHub Actions / test (windows-latest)

invalid operation: before.VarsDiagnostics["example.tfvars"][0] == after.VarsDiagnostics["example.tfvars"][0] (slice can only be compared to nil)

Check failure on line 1158 in internal/terraform/module/module_ops_test.go

View workflow job for this annotation

GitHub Actions / test (windows-latest)

cannot use "example.tfvars" (untyped string constant) as "github.com/hashicorp/terraform-ls/internal/terraform/ast".DiagnosticSource value in map index

Check failure on line 1158 in internal/terraform/module/module_ops_test.go

View workflow job for this annotation

GitHub Actions / test (windows-latest)

cannot use 0 (untyped int constant) as "github.com/hashicorp/terraform-ls/internal/terraform/ast".VarsFilename value in map index

Check failure on line 1158 in internal/terraform/module/module_ops_test.go

View workflow job for this annotation

GitHub Actions / test (windows-latest)

cannot use "example.tfvars" (untyped string constant) as "github.com/hashicorp/terraform-ls/internal/terraform/ast".DiagnosticSource value in map index

Check failure on line 1158 in internal/terraform/module/module_ops_test.go

View workflow job for this annotation

GitHub Actions / test (windows-latest)

cannot use 0 (untyped int constant) as "github.com/hashicorp/terraform-ls/internal/terraform/ast".VarsFilename value in map index

Check failure on line 1158 in internal/terraform/module/module_ops_test.go

View workflow job for this annotation

GitHub Actions / test (macos-latest)

invalid operation: before.VarsDiagnostics["example.tfvars"][0] == after.VarsDiagnostics["example.tfvars"][0] (slice can only be compared to nil)

Check failure on line 1158 in internal/terraform/module/module_ops_test.go

View workflow job for this annotation

GitHub Actions / test (macos-latest)

cannot use "example.tfvars" (untyped string constant) as "github.com/hashicorp/terraform-ls/internal/terraform/ast".DiagnosticSource value in map index

Check failure on line 1158 in internal/terraform/module/module_ops_test.go

View workflow job for this annotation

GitHub Actions / test (macos-latest)

cannot use 0 (untyped int constant) as "github.com/hashicorp/terraform-ls/internal/terraform/ast".VarsFilename value in map index

Check failure on line 1158 in internal/terraform/module/module_ops_test.go

View workflow job for this annotation

GitHub Actions / test (macos-latest)

cannot use "example.tfvars" (untyped string constant) as "github.com/hashicorp/terraform-ls/internal/terraform/ast".DiagnosticSource value in map index

Check failure on line 1158 in internal/terraform/module/module_ops_test.go

View workflow job for this annotation

GitHub Actions / test (macos-latest)

cannot use 0 (untyped int constant) as "github.com/hashicorp/terraform-ls/internal/terraform/ast".VarsFilename value in map index
t.Fatal("diags should mismatch")
}
}

func gzipCompressBytes(t *testing.T, b []byte) []byte {
var compressedBytes bytes.Buffer
gw := gzip.NewWriter(&compressedBytes)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
variable "image_id" {
type = string
}

# this is supposed to generate a diagnostic
lalalalal "goo"
15 changes: 15 additions & 0 deletions internal/terraform/parser/variables.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package parser
import (
"path/filepath"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/terraform-ls/internal/terraform/ast"
)

Expand Down Expand Up @@ -48,3 +49,17 @@ func ParseVariableFiles(fs FS, modPath string) (ast.VarsFiles, ast.VarsDiags, er

return files, diags, nil
}

func ParseVariableFile(fs FS, filePath string) (*hcl.File, hcl.Diagnostics, error) {
src, err := fs.ReadFile(filePath)
if err != nil {
return nil, nil, err
}

name := filepath.Base(filePath)
filename := ast.VarsFilename(name)

f, pDiags := parseFile(src, filename)

return f, pDiags, nil
}

0 comments on commit d9cae9e

Please sign in to comment.