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

Update ParseModuleConfiguration to only parse changed file (*.tf) #1404

Merged
merged 10 commits into from
Sep 22, 2023
Merged
Show file tree
Hide file tree
Changes from 4 commits
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
4 changes: 4 additions & 0 deletions internal/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,7 @@ func IsLanguageId(ctx context.Context, expectedLangId string) bool {
}
return langId == expectedLangId
}

func (ctxData RPCContextData) IsDidChangeRequest() bool {
return ctxData.Method == "textDocument/didChange"
}
2 changes: 2 additions & 0 deletions internal/decoder/decoder_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (

"github.com/hashicorp/hcl-lang/decoder"
"github.com/hashicorp/hcl-lang/lang"
lsctx "github.com/hashicorp/terraform-ls/internal/context"
idecoder "github.com/hashicorp/terraform-ls/internal/decoder"
"github.com/hashicorp/terraform-ls/internal/state"
"github.com/hashicorp/terraform-ls/internal/terraform/module"
Expand Down Expand Up @@ -60,6 +61,7 @@ func TestDecoder_CodeLensesForFile_concurrencyBug(t *testing.T) {
if err != nil {
t.Error(err)
}
ctx = lsctx.WithRPCContext(ctx, lsctx.RPCContextData{})
err = module.ParseModuleConfiguration(ctx, mapFs, ss.Modules, dirName)
if err != nil {
t.Error(err)
Expand Down
16 changes: 16 additions & 0 deletions internal/terraform/ast/module.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,14 @@ func (mf ModFiles) AsMap() map[string]*hcl.File {
return m
}

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

type ModDiags map[ModFilename]hcl.Diagnostics

func ModDiagsFromMap(m map[string]hcl.Diagnostics) ModDiags {
Expand Down Expand Up @@ -83,6 +91,14 @@ func (md ModDiags) AsMap() map[string]hcl.Diagnostics {
return m
}

func (md ModDiags) Copy() ModDiags {
m := make(ModDiags, len(md))
for name, diags := range md {
m[name] = diags
}
return m
}

func (md ModDiags) Count() int {
count := 0
for _, diags := range md {
Expand Down
40 changes: 36 additions & 4 deletions internal/terraform/module/module_ops.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,27 @@ import (
"io"
"io/fs"
"log"
"path/filepath"
"time"

"github.com/hashicorp/go-multierror"
"github.com/hashicorp/go-version"
"github.com/hashicorp/hcl-lang/decoder"
"github.com/hashicorp/hcl-lang/lang"
tfjson "github.com/hashicorp/terraform-json"
lsctx "github.com/hashicorp/terraform-ls/internal/context"
idecoder "github.com/hashicorp/terraform-ls/internal/decoder"
"github.com/hashicorp/terraform-ls/internal/document"
"github.com/hashicorp/terraform-ls/internal/job"
ilsp "github.com/hashicorp/terraform-ls/internal/lsp"
"github.com/hashicorp/terraform-ls/internal/registry"
"github.com/hashicorp/terraform-ls/internal/schemas"
"github.com/hashicorp/terraform-ls/internal/state"
"github.com/hashicorp/terraform-ls/internal/terraform/ast"
"github.com/hashicorp/terraform-ls/internal/terraform/datadir"
op "github.com/hashicorp/terraform-ls/internal/terraform/module/operation"
"github.com/hashicorp/terraform-ls/internal/terraform/parser"
"github.com/hashicorp/terraform-ls/internal/uri"
tfaddr "github.com/hashicorp/terraform-registry-address"
"github.com/hashicorp/terraform-schema/earlydecoder"
"github.com/hashicorp/terraform-schema/module"
Expand Down Expand Up @@ -359,19 +363,47 @@ func ParseModuleConfiguration(ctx context.Context, fs ReadOnlyFS, modStore *stat

// 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.ModuleParsingState != op.OpStateUnknown && !job.IgnoreState(ctx) {
return job.StateNotChangedErr{Dir: document.DirHandleFromPath(modPath)}
}

err = modStore.SetModuleParsingState(modPath, op.OpStateLoading)
radeksimko marked this conversation as resolved.
Show resolved Hide resolved
var files ast.ModFiles
var diags ast.ModDiags
rpcContext := lsctx.RPCContext(ctx)
// Only parse the file that's being changed/opened, unless this is 1st-time parsing
if mod.ModuleParsingState == op.OpStateLoaded && rpcContext.IsDidChangeRequest() {
jpogran marked this conversation as resolved.
Show resolved Hide resolved
// the file has already been parsed, so only examine this file and not the whole module
filePath, err := uri.PathFromURI(rpcContext.URI)
if err != nil {
return err
}
fileName := filepath.Base(filePath)

f, fDiags, err := parser.ParseModuleFile(fs, filePath)
if err != nil {
return err
}
existingFiles := mod.ParsedModuleFiles.Copy()
existingFiles[ast.ModFilename(fileName)] = f
files = existingFiles

existingDiags := mod.ModuleDiagnostics.Copy()
existingDiags[ast.ModFilename(fileName)] = fDiags
diags = existingDiags
} else {
// this is the first time file is opened so parse the whole module
files, diags, err = parser.ParseModuleFiles(fs, modPath)
}

if err != nil {
return err
}

files, diags, err := parser.ParseModuleFiles(fs, modPath)
err = modStore.SetModuleParsingState(modPath, op.OpStateLoading)
if err != nil {
return err
}

sErr := modStore.UpdateParsedModuleFiles(modPath, files, err)
if sErr != nil {
Expand Down
82 changes: 82 additions & 0 deletions internal/terraform/module/module_ops_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@ import (
"github.com/hashicorp/go-version"
"github.com/hashicorp/hcl-lang/lang"
tfjson "github.com/hashicorp/terraform-json"
lsctx "github.com/hashicorp/terraform-ls/internal/context"
"github.com/hashicorp/terraform-ls/internal/document"
"github.com/hashicorp/terraform-ls/internal/filesystem"
"github.com/hashicorp/terraform-ls/internal/job"
"github.com/hashicorp/terraform-ls/internal/registry"
"github.com/hashicorp/terraform-ls/internal/state"
"github.com/hashicorp/terraform-ls/internal/terraform/exec"
"github.com/hashicorp/terraform-ls/internal/terraform/module/operation"
"github.com/hashicorp/terraform-ls/internal/uri"
tfaddr "github.com/hashicorp/terraform-registry-address"
tfregistry "github.com/hashicorp/terraform-schema/registry"
"github.com/stretchr/testify/mock"
Expand All @@ -56,6 +58,7 @@ func TestGetModuleDataFromRegistry_singleModule(t *testing.T) {
}

fs := filesystem.NewFilesystem(ss.DocumentStore)
ctx = lsctx.WithRPCContext(ctx, lsctx.RPCContextData{})
err = ParseModuleConfiguration(ctx, fs, ss.Modules, modPath)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -128,6 +131,7 @@ func TestGetModuleDataFromRegistry_moduleNotFound(t *testing.T) {
}

fs := filesystem.NewFilesystem(ss.DocumentStore)
ctx = lsctx.WithRPCContext(ctx, lsctx.RPCContextData{})
err = ParseModuleConfiguration(ctx, fs, ss.Modules, modPath)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -228,6 +232,7 @@ func TestGetModuleDataFromRegistry_apiTimeout(t *testing.T) {
}

fs := filesystem.NewFilesystem(ss.DocumentStore)
ctx = lsctx.WithRPCContext(ctx, lsctx.RPCContextData{})
err = ParseModuleConfiguration(ctx, fs, ss.Modules, modPath)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -522,6 +527,7 @@ func TestParseProviderVersions_multipleVersions(t *testing.T) {
if err != nil {
t.Fatal(err)
}
ctx = lsctx.WithRPCContext(ctx, lsctx.RPCContextData{})
err = ParseModuleConfiguration(ctx, fs, ss.Modules, modPathFirst)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -691,6 +697,7 @@ func TestPreloadEmbeddedSchema_basic(t *testing.T) {
if err != nil {
t.Fatal(err)
}
ctx = lsctx.WithRPCContext(ctx, lsctx.RPCContextData{})
err = ParseModuleConfiguration(ctx, cfgFS, ss.Modules, modPath)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -761,6 +768,7 @@ func TestPreloadEmbeddedSchema_unknownProviderOnly(t *testing.T) {
if err != nil {
t.Fatal(err)
}
ctx = lsctx.WithRPCContext(ctx, lsctx.RPCContextData{})
err = ParseModuleConfiguration(ctx, cfgFS, ss.Modules, modPath)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -824,6 +832,7 @@ func TestPreloadEmbeddedSchema_idempotency(t *testing.T) {
if err != nil {
t.Fatal(err)
}
ctx = lsctx.WithRPCContext(ctx, lsctx.RPCContextData{})
err = ParseModuleConfiguration(ctx, cfgFS, ss.Modules, modPath)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -903,6 +912,7 @@ func TestPreloadEmbeddedSchema_raceCondition(t *testing.T) {
if err != nil {
t.Fatal(err)
}
ctx = lsctx.WithRPCContext(ctx, lsctx.RPCContextData{})
err = ParseModuleConfiguration(ctx, cfgFS, ss.Modules, modPath)
if err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -931,6 +941,78 @@ func TestPreloadEmbeddedSchema_raceCondition(t *testing.T) {
wg.Wait()
}

func TestParseModuleConfiguration(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.WithRPCContext(ctx, lsctx.RPCContextData{})
err = ParseModuleConfiguration(ctx, testFs, ss.Modules, singleFileModulePath)
if err != nil {
t.Fatal(err)
}

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

// ignore job state
ctx = job.WithIgnoreState(ctx, true)

// say we're coming from did_change request
fooURI, _ := filepath.Abs(filepath.Join(singleFileModulePath, "foo.tf"))
jpogran marked this conversation as resolved.
Show resolved Hide resolved
x := lsctx.RPCContextData{
Method: "textDocument/didChange",
URI: uri.FromPath(fooURI),
}
radeksimko marked this conversation as resolved.
Show resolved Hide resolved
ctx = lsctx.WithRPCContext(ctx, x)
err = ParseModuleConfiguration(ctx, testFs, ss.Modules, singleFileModulePath)
if err != nil {
t.Fatal(err)
}

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

// test if foo.tf is not the same as first seen
if before.ParsedModuleFiles["foo.tf"] == after.ParsedModuleFiles["foo.tf"] {
t.Fatal("file should mismatch")
}

// test if main.tf is the same as first seen
if before.ParsedModuleFiles["main.tf"] != after.ParsedModuleFiles["main.tf"] {
t.Fatal("file mismatch")
}

// examine diags should change for foo.tf
if before.ModuleDiagnostics["foo.tf"][0] == after.ModuleDiagnostics["foo.tf"][0] {
t.Fatal("diags should mismatch")
}

// examine diags should change for main.tf
if before.ModuleDiagnostics["main.tf"][0] != after.ModuleDiagnostics["main.tf"][0] {
t.Fatal("diags should match")
}
}

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
@@ -0,0 +1,3 @@
variable "another" {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
variable "gogo" {

}


variable "awesome" {


Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
variable "wakka" {


16 changes: 16 additions & 0 deletions internal/terraform/parser/module.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 @@ -52,3 +53,18 @@ func ParseModuleFiles(fs FS, modPath string) (ast.ModFiles, ast.ModDiags, error)

return files, diags, nil
}

func ParseModuleFile(fs FS, filePath string) (*hcl.File, hcl.Diagnostics, error) {
src, err := fs.ReadFile(filePath)
if err != nil {
// If a file isn't accessible, return
return nil, nil, err
}

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

f, pDiags := parseFile(src, filename)

return f, pDiags, nil
}